Merge branch 'master' into devel

This commit is contained in:
Matthias Koefferlein 2025-01-19 22:04:55 +01:00
commit 33b54f3f88
27 changed files with 556 additions and 76 deletions

View File

@ -1,3 +1,12 @@
0.29.11 (2025-01-17):
* Bug: %GITHUB%/issues/1948 Crash by instantiate a Cell in a Library-Cell
* Bug: %GITHUB%/issues/1953 Callback_impl & coerce_parameters_impl parameter display issue
* Bug: %GITHUB%/issues/1955 Locked layout after certain operations
* Bug: %GITHUB%/issues/1959 Color corrections not working properly for images with color mapping / float values
* Enhancement: %GITHUB%/issues/1963 Add lock to Cell
* Bug: %GITHUB%/issues/1967 Possible net tracer bug in point-and-click net trace
* Enhancement: %GITHUB%/issues/1971 Suppress warnings written with tl::warn from Python
0.29.10 (2024-12-03):
* Bug: %GITHUB%/issues/1941 Crash with the navigator open
* Bug: %GITHUB%/issues/1942 Syntax error in pyi stubs

View File

@ -1,3 +1,10 @@
klayout (0.29.11-1) unstable; urgency=low
* New features and bugfixes
- See changelog
-- Matthias Köfferlein <matthias@koefferlein.de> Fri, 17 Jan 2025 10:20:14 +0100
klayout (0.29.10-1) unstable; urgency=low
* New features and bugfixes

View File

@ -93,7 +93,7 @@ Cell::box_type Cell::ms_empty_box = Cell::box_type ();
Cell::Cell (cell_index_type ci, db::Layout &l)
: db::Object (l.manager ()),
m_cell_index (ci), mp_layout (&l), m_instances (this), m_prop_id (0), m_hier_levels (0),
m_bbox_needs_update (false), m_ghost_cell (false),
m_bbox_needs_update (false), m_locked (false), m_ghost_cell (false),
mp_last (0), mp_next (0)
{
// .. nothing yet
@ -125,6 +125,7 @@ Cell::operator= (const Cell &d)
}
m_ghost_cell = d.m_ghost_cell;
m_locked = d.m_locked;
m_instances = d.m_instances;
m_bbox = d.m_bbox;
m_bboxes = d.m_bboxes;
@ -138,6 +139,7 @@ Cell::operator= (const Cell &d)
Cell::~Cell ()
{
m_locked = false;
clear_shapes ();
}
@ -180,6 +182,8 @@ Cell::empty () const
void
Cell::clear (unsigned int index)
{
check_locked ();
shapes_map::iterator s = m_shapes_map.find(index);
if (s != m_shapes_map.end() && ! s->second.empty ()) {
mp_layout->invalidate_bboxes (index); // HINT: must come before the change is done!
@ -191,6 +195,8 @@ Cell::clear (unsigned int index)
void
Cell::clear (unsigned int index, unsigned int types)
{
check_locked ();
shapes_map::iterator s = m_shapes_map.find(index);
if (s != m_shapes_map.end() && ! s->second.empty ()) {
mp_layout->invalidate_bboxes (index); // HINT: must come before the change is done!
@ -241,6 +247,8 @@ Cell::index_of_shapes (const Cell::shapes_type *shapes) const
void
Cell::clear_shapes ()
{
check_locked ();
mp_layout->invalidate_bboxes (std::numeric_limits<unsigned int>::max ()); // HINT: must come before the change is done!
clear_shapes_no_invalidate ();
}
@ -345,6 +353,8 @@ Cell::update_bbox (unsigned int layers)
void
Cell::copy (unsigned int src, unsigned int dest)
{
check_locked ();
if (src != dest) {
shapes (dest).insert (shapes (src));
} else {
@ -359,6 +369,8 @@ Cell::copy (unsigned int src, unsigned int dest)
void
Cell::copy (unsigned int src, unsigned int dest, unsigned int types)
{
check_locked ();
if (src != dest) {
shapes (dest).insert (shapes (src), types);
} else {
@ -373,6 +385,8 @@ Cell::copy (unsigned int src, unsigned int dest, unsigned int types)
void
Cell::move (unsigned int src, unsigned int dest)
{
check_locked ();
if (src != dest) {
copy (src, dest);
clear (src);
@ -382,6 +396,8 @@ Cell::move (unsigned int src, unsigned int dest)
void
Cell::move (unsigned int src, unsigned int dest, unsigned int types)
{
check_locked ();
if (src != dest) {
copy (src, dest, types);
clear (src, types);
@ -391,6 +407,8 @@ Cell::move (unsigned int src, unsigned int dest, unsigned int types)
void
Cell::swap (unsigned int i1, unsigned int i2)
{
check_locked ();
if (i1 != i2) {
if (manager () && manager ()->transacting ()) {
@ -787,6 +805,14 @@ Cell::set_name (const std::string &name)
layout ()->rename_cell (cell_index (), name.c_str ());
}
void
Cell::check_locked () const
{
if (m_locked) {
throw tl::Exception (tl::to_string (tr ("Cell '%s' cannot be modified as it is locked")), get_basic_name ());
}
}
void
Cell::copy_shapes (const db::Cell &source_cell, const db::LayerMapping &layer_mapping)
{
@ -803,6 +829,8 @@ Cell::copy_shapes (const db::Cell &source_cell, const db::LayerMapping &layer_ma
throw tl::Exception (tl::to_string (tr ("Source cell does not reside in a layout")));
}
check_locked ();
if (target_layout != source_layout) {
db::ICplxTrans trans (source_layout->dbu () / target_layout->dbu ());
for (std::map<unsigned int, unsigned int>::const_iterator lm = layer_mapping.begin (); lm != layer_mapping.end (); ++lm) {
@ -826,6 +854,8 @@ Cell::copy_shapes (const db::Cell &source_cell)
throw tl::Exception (tl::to_string (tr ("Cell does not reside in a layout")));
}
check_locked ();
if (target_layout != source_cell.layout ()) {
if (! source_cell.layout ()) {
throw tl::Exception (tl::to_string (tr ("Source cell does not reside in a layout")));
@ -850,6 +880,8 @@ Cell::copy_instances (const db::Cell &source_cell)
throw tl::Exception (tl::to_string (tr ("Cells do not reside in the same layout")));
}
check_locked ();
for (db::Cell::const_iterator i = source_cell.begin (); ! i.at_end (); ++i) {
insert (*i);
}
@ -871,6 +903,8 @@ Cell::copy_tree (const db::Cell &source_cell)
throw tl::Exception (tl::to_string (tr ("Source cell does not reside in a layout")));
}
check_locked ();
db::ICplxTrans trans (source_layout->dbu () / target_layout->dbu ());
db::CellMapping cm;
@ -902,6 +936,8 @@ Cell::copy_tree_shapes (const db::Cell &source_cell, const db::CellMapping &cm)
throw tl::Exception (tl::to_string (tr ("Source cell does not reside in a layout")));
}
check_locked ();
db::ICplxTrans trans (source_layout->dbu () / target_layout->dbu ());
db::LayerMapping lm;
@ -928,6 +964,8 @@ Cell::copy_tree_shapes (const db::Cell &source_cell, const db::CellMapping &cm,
throw tl::Exception (tl::to_string (tr ("Source cell does not reside in a layout")));
}
check_locked ();
db::ICplxTrans trans (source_layout->dbu () / target_layout->dbu ());
std::vector <db::cell_index_type> source_cells;
@ -951,6 +989,8 @@ Cell::move_shapes (db::Cell &source_cell, const db::LayerMapping &layer_mapping)
throw tl::Exception (tl::to_string (tr ("Source cell does not reside in a layout")));
}
check_locked ();
if (target_layout != source_layout) {
db::ICplxTrans trans (source_layout->dbu () / target_layout->dbu ());
for (std::map<unsigned int, unsigned int>::const_iterator lm = layer_mapping.begin (); lm != layer_mapping.end (); ++lm) {
@ -976,6 +1016,8 @@ Cell::move_shapes (db::Cell &source_cell)
throw tl::Exception (tl::to_string (tr ("Cell does not reside in a layout")));
}
check_locked ();
if (target_layout != source_cell.layout ()) {
if (! source_cell.layout ()) {
throw tl::Exception (tl::to_string (tr ("Source cell does not reside in a layout")));
@ -1001,6 +1043,8 @@ Cell::move_instances (db::Cell &source_cell)
throw tl::Exception (tl::to_string (tr ("Cells do not reside in the same layout")));
}
check_locked ();
for (db::Cell::const_iterator i = source_cell.begin (); ! i.at_end (); ++i) {
insert (*i);
}
@ -1024,6 +1068,8 @@ Cell::move_tree (db::Cell &source_cell)
throw tl::Exception (tl::to_string (tr ("Source cell does not reside in a layout")));
}
check_locked ();
db::ICplxTrans trans (source_layout->dbu () / target_layout->dbu ());
db::CellMapping cm;
@ -1057,6 +1103,8 @@ Cell::move_tree_shapes (db::Cell &source_cell, const db::CellMapping &cm)
throw tl::Exception (tl::to_string (tr ("Source cell does not reside in a layout")));
}
check_locked ();
db::ICplxTrans trans (source_layout->dbu () / target_layout->dbu ());
db::LayerMapping lm;
@ -1083,6 +1131,8 @@ Cell::move_tree_shapes (db::Cell &source_cell, const db::CellMapping &cm, const
throw tl::Exception (tl::to_string (tr ("Source cell does not reside in a layout")));
}
check_locked ();
db::ICplxTrans trans (source_layout->dbu () / target_layout->dbu ());
std::vector <db::cell_index_type> source_cells;

View File

@ -831,6 +831,13 @@ public:
*/
virtual void update (ImportLayerMapping * /*layer_mapping*/ = 0) { }
/**
* @brief Checks if the cell is locked
*
* This method throws an exception if the cell is locked.
*/
void check_locked () const;
/**
* @brief Tell, if this cell is a proxy cell
*
@ -902,6 +909,27 @@ public:
m_ghost_cell = g;
}
/**
* @brief Gets a value indicating whether the cell is locked
*
* A locked cell cannot be modified in terms of instances or shapes.
* The name of a locked cell can be changed though.
*/
bool is_locked () const
{
return m_locked;
}
/**
* @brief Sets the locked state of the cell
*
* See \is_locked for details about locked state.
*/
void set_locked (bool f)
{
m_locked = f;
}
/**
* @brief Returns a value indicating whether the cell is empty
*
@ -1075,8 +1103,9 @@ private:
db::properties_id_type m_prop_id;
// packed fields
unsigned int m_hier_levels : 30;
unsigned int m_hier_levels : 29;
bool m_bbox_needs_update : 1;
bool m_locked : 1;
bool m_ghost_cell : 1;
static box_type ms_empty_box;

View File

@ -1035,8 +1035,10 @@ Instances::layout () const
void
Instances::invalidate_insts ()
{
if (cell ()) {
cell ()->invalidate_insts ();
db::Cell *cp = cell ();
if (cp) {
cp->check_locked ();
cp->invalidate_insts ();
}
set_instance_by_cell_index_needs_made (true);

View File

@ -867,6 +867,7 @@ Layout::delete_cells (const std::set<cell_index_type> &cells_to_delete)
std::set <cell_index_type> pcs;
for (std::set<cell_index_type>::const_iterator c = cells_to_delete.begin (); c != cells_to_delete.end (); ++c) {
const db::Cell &cref = cell (*c);
cref.check_locked ();
for (db::Cell::parent_cell_iterator pc = cref.begin_parent_cells (); pc != cref.end_parent_cells (); ++pc) {
pcs.insert (*pc);
}
@ -944,6 +945,7 @@ void
Layout::delete_cell (cell_index_type id)
{
db::Cell &cref = cell (id);
cref.check_locked ();
std::vector <cell_index_type> pcs;
for (db::Cell::parent_cell_iterator pc = cref.begin_parent_cells (); pc != cref.end_parent_cells (); ++pc) {
@ -1134,6 +1136,8 @@ Layout::flatten (const db::Cell &source_cell, db::Cell &target_cell, const db::I
void
Layout::flatten (db::Cell &cell_to_flatten, int levels, bool prune)
{
cell_to_flatten.check_locked ();
std::set<db::cell_index_type> direct_children;
if (prune) {
// save direct children

View File

@ -297,12 +297,16 @@ Shapes::array_repository () const
void
Shapes::invalidate_state ()
{
db::Cell *cp = cell ();
if (cp) {
cp->check_locked ();
}
if (! is_dirty ()) {
set_dirty (true);
if (layout () && cell ()) {
unsigned int index = cell ()->index_of_shapes (this);
if (cp && cp->layout ()) {
unsigned int index = cp->index_of_shapes (this);
if (index != std::numeric_limits<unsigned int>::max ()) {
layout ()->invalidate_bboxes (index);
cp->layout ()->invalidate_bboxes (index);
}
// property ID change is implied
layout ()->invalidate_prop_ids ();

View File

@ -2863,6 +2863,28 @@ Class<db::Cell> decl_Cell ("db", "Cell",
"\n"
"@return A list of cell indices.\n"
) +
gsi::method ("is_locked?", &db::Cell::is_locked,
"@brief Gets a value indicating whether the cell is locked\n"
"\n"
"Locked cells cannot be modified in terms of instances (children) and shapes. "
"Locked cells can still be renamed, but cannot be deleted or cleared.\n"
"Among other things, these features are disabled too: layer operations, copy of instances or shapes, "
"flattening or pruning.\n"
"\n"
"However, wiping the layout entirely with \\Layout#clear is always possible, even if cells are locked.\n"
"\n"
"Use \\locked= to set the locked state of the cell.\n"
"\n"
"The lock feature has been introduced in version 0.29.11."
) +
gsi::method ("locked=", &db::Cell::set_locked, gsi::arg ("l"),
"@brief Locks or unlocks the cell\n"
"\n"
"Set this predicate to 'true' to lock the cell and to 'false' to unlock it.\n"
"See \\is_locked? for details about the lock feature.\n"
"\n"
"The lock feature has been introduced in version 0.29.11."
) +
gsi::method ("bbox", (const db::Cell::box_type &(db::Cell::*) () const) &db::Cell::bbox,
"@brief Gets the bounding box of the cell\n"
"\n"

View File

@ -105,11 +105,14 @@ Class<Logger> decl_Logger ("tl", "Logger",
"Level 0 is silent, levels 10, 20, 30 etc. denote levels with increasing verbosity. "
"11, 21, 31 .. are sublevels which also enable timing logs in addition to messages."
) +
gsi::method ("verbosity=", &Logger::set_verbosity, gsi::arg ("v"),
gsi::method ("verbosity=|set_verbosity", &Logger::set_verbosity, gsi::arg ("v"),
"@brief Sets the verbosity level for the application\n"
"\n"
"See \\verbosity for a definition of the verbosity levels. Please note that this method "
"changes the verbosity level for the whole application.\n"
"\n"
"The 'set_verbosity' alias has been introduced in version 0.29.11 as class attributes "
"are not always available in Python."
),
"@brief A logger\n"
"\n"

View File

@ -375,6 +375,18 @@ A *A::a20_get ()
return a_inst.get ();
}
static int s_sp = 0;
int A::sp_i_get ()
{
return s_sp;
}
void A::sp_i_set (int v)
{
s_sp = v + 1;
}
// ----------------------------------------------------------------
// Implementation of B
@ -1253,6 +1265,8 @@ static gsi::Class<A> decl_a ("", "A",
gsi::method ("a9b", &A::a9b) +
gsi::method ("a20", &A::a20) +
gsi::method ("a20_get", &A::a20_get) +
gsi::method ("sp_i", &A::sp_i_get) +
gsi::method ("sp_i=", &A::sp_i_set) +
gsi::method ("to_s", &A::to_s) +
gsi::iterator ("a6", &A::a6b, &A::a6e) +
gsi::iterator ("a7", &A::a7b, &A::a7e) +

View File

@ -415,6 +415,10 @@ struct A
std::string to_s () const;
// static (class) properties
static int sp_i_get ();
static void sp_i_set (int v);
// members
std::vector<double> m_d;
int n;

View File

@ -322,9 +322,6 @@ klayout_main (int &argc, char **argv)
int v = 0;
tl::from_string (argv [++i], v);
if (v < 0) {
v = 0;
}
tl::verbosity (v);
}

View File

@ -31,6 +31,16 @@
</property>
<item row="0" column="1">
<widget class="QComboBox" name="verbosity_cbx">
<item>
<property name="text">
<string>No warnings, no errors</string>
</property>
</item>
<item>
<property name="text">
<string>No warnings</string>
</property>
</item>
<item>
<property name="text">
<string>Silent</string>

View File

@ -312,9 +312,6 @@ ApplicationBase::parse_cmd (int &argc, char **argv)
int v = 0;
tl::from_string (args [++i], v);
if (v < 0) {
v = 0;
}
tl::verbosity (v);
} else if (a == "-k" && (i + 1) < argc) {
@ -1065,7 +1062,7 @@ ApplicationBase::usage ()
r += tl::to_string (QObject::tr (" -b Batch mode (same as -zz -nc -rx)")) + "\n";
r += tl::to_string (QObject::tr (" -c <config file> Use this configuration file")) + "\n";
r += tl::to_string (QObject::tr (" -nc Don't use a configuration file (implies -t)")) + "\n";
r += tl::to_string (QObject::tr (" -d <debug level> Set debug level")) + "\n";
r += tl::to_string (QObject::tr (" -d <log level> Set log level")) + "\n";
r += tl::to_string (QObject::tr (" -e Editable mode (allow editing of files)")) + "\n";
r += tl::to_string (QObject::tr (" -ne Readonly mode (editing of files is disabled)")) + "\n";
r += tl::to_string (QObject::tr (" -gr <file name> Record GUI test file")) + "\n";

View File

@ -107,9 +107,9 @@ LogReceiver::begin ()
// LogFile implementation
LogFile::LogFile (size_t max_entries, bool register_global)
: m_error_receiver (this, 0, &LogFile::add_error),
: m_error_receiver (this, -10, &LogFile::add_error),
m_warn_receiver (this, 0, &LogFile::add_warn),
m_log_receiver (this, 0, &LogFile::add_info),
m_log_receiver (this, 10, &LogFile::add_info),
m_info_receiver (this, 0, &LogFile::add_info),
m_max_entries (max_entries),
m_generation_id (0),
@ -347,7 +347,7 @@ LogViewerDialog::LogViewerDialog (QWidget *parent, bool register_global, bool in
verbosity_cbx->hide ();
verbosity_label->hide ();
} else {
verbosity_cbx->setCurrentIndex (std::min (4, tl::verbosity () / 10));
verbosity_cbx->setCurrentIndex (std::max (-2, std::min (4, tl::verbosity () / 10)) + 2);
connect (verbosity_cbx, SIGNAL (currentIndexChanged (int)), this, SLOT (verbosity_changed (int)));
}
@ -371,7 +371,7 @@ LogViewerDialog::LogViewerDialog (QWidget *parent, bool register_global, bool in
void
LogViewerDialog::verbosity_changed (int index)
{
tl::verbosity (index * 10 + 1);
tl::verbosity ((index - 2) * 10 + 1);
}
// -----------------------------------------------------------------

View File

@ -1454,7 +1454,7 @@ NetTracer::determine_interactions (const db::Polygon &seed, const NetTracerShape
db::Polygon::area_type poly_area = seed.area_upper_manhattan_bound ();
db::Polygon::area_type box_area = seed.box ().area ();
if (poly_area == box_area && seed.vertices () == 4) {
if (seed.is_box ()) {
// The polygon is a box
determine_interactions (seed.box (), shape, layers, delivery);

View File

@ -394,3 +394,15 @@ TEST(9)
run_test (_this, file, tc, db::LayerProperties (8, 0), db::Point (3000, 6800), file_au, "A");
}
// issue #1967
TEST(10)
{
std::string file = "t10.oas.gz";
std::string file_au = "t10_net.oas.gz";
db::NetTracerConnectivity tc;
tc.add (connection ("1", "2"));
run_test (_this, file, tc, db::LayerProperties (1, 0), db::Point (300000, 7000), file_au, "");
}

View File

@ -1285,7 +1285,14 @@ property_setter_impl (int mid, PyObject *self, PyObject *value)
meth->call (obj, arglist, retlist);
return get_return_value (p, retlist, meth, heap);
PyObject *ret = get_return_value (p, retlist, meth, heap);
if (ret == NULL) {
Py_INCREF (Py_None);
ret = Py_None;
}
return ret;
}
}

View File

@ -426,7 +426,11 @@ public:
doc += "\n\n";
}
doc += (*m)->doc ();
mp_module->add_python_doc (*m, tl::sprintf (tl::to_string (tr ("The object exposes a readable attribute '%s'. This is the getter")), name));
if (! is_static) {
mp_module->add_python_doc (*m, tl::sprintf (tl::to_string (tr ("The object exposes a readable attribute '%s'. This is the getter")), name));
} else {
mp_module->add_python_doc (*m, tl::sprintf (tl::to_string (tr ("The class exposes a readable attribute '%s'. This is the getter")), name));
}
}
for (MethodTableEntry::method_iterator m = begin_setters; m != end_setters; ++m) {
@ -434,7 +438,11 @@ public:
doc += "\n\n";
}
doc += (*m)->doc ();
mp_module->add_python_doc (*m, tl::sprintf (tl::to_string (tr ("The object exposes a writable attribute '%s'. This is the setter")), name));
if (! is_static) {
mp_module->add_python_doc (*m, tl::sprintf (tl::to_string (tr ("The object exposes a writable attribute '%s'. This is the setter")), name));
} else {
mp_module->add_python_doc (*m, tl::sprintf (tl::to_string (tr ("The class exposes a writable attribute '%s'. This setter may not be available in Python")), name));
}
}
PythonRef attr;

View File

@ -832,6 +832,27 @@ class Cell:
This method has been introduced in version 0.20.
"""
locked: bool
r"""
Getter:
@brief Gets a value indicating whether the cell is locked
Locked cells cannot be modified in terms of instances (children) and shapes. Locked cells can still be renamed, but cannot be deleted or cleared.
Among other things, these features are disabled too: layer operations, copy of instances or shapes, flattening or pruning.
However, wiping the layout entirely with \Layout#clear is always possible, even if cells are locked.
Use \locked= to set the locked state of the cell.
The lock feature has been introduced in version 0.29.11.
Setter:
@brief Locks or unlocks the cell
Set this predicate to 'true' to lock the cell and to 'false' to unlock it.
See \is_locked? for details about the lock feature.
The lock feature has been introduced in version 0.29.11.
"""
name: str
r"""
Getter:
@ -1912,6 +1933,20 @@ class Cell:
This method has been introduced in version 0.22.
"""
...
def is_locked(self) -> bool:
r"""
@brief Gets a value indicating whether the cell is locked
Locked cells cannot be modified in terms of instances (children) and shapes. Locked cells can still be renamed, but cannot be deleted or cleared.
Among other things, these features are disabled too: layer operations, copy of instances or shapes, flattening or pruning.
However, wiping the layout entirely with \Layout#clear is always possible, even if cells are locked.
Use \locked= to set the locked state of the cell.
The lock feature has been introduced in version 0.29.11.
"""
...
@overload
def is_pcell_variant(self) -> bool:
r"""
@ -30230,11 +30265,11 @@ class Instance:
Starting with version 0.25 the displacement is of vector type.
Setter:
@brief Sets the displacement vector for the 'b' axis
@brief Sets the displacement vector for the 'b' axis in micrometer units
If the instance was not an array instance before it is made one.
Like \b= with an integer displacement, this method will set the displacement vector but it accepts a vector in micrometer units that is of \DVector type. The vector will be translated to database units internally.
This method has been introduced in version 0.23. Starting with version 0.25 the displacement is of vector type.
This method has been introduced in version 0.25.
"""
cell: Cell
r"""
@ -30408,9 +30443,10 @@ class Instance:
@brief Gets the transformation of the instance or the first instance in the array
The transformation returned is only valid if the array does not represent a complex transformation array
Setter:
@brief Sets the transformation of the instance or the first instance in the array
@brief Sets the transformation of the instance or the first instance in the array (in micrometer units)
This method sets the transformation the same way as \cplx_trans=, but the displacement of this transformation is given in micrometer units. It is internally translated into database units.
This method has been introduced in version 0.23.
This method has been introduced in version 0.25.
"""
@classmethod
def new(cls) -> Instance:
@ -41367,15 +41403,15 @@ class NetPinRef:
@overload
def net(self) -> Net:
r"""
@brief Gets the net this pin reference is attached to.
@brief Gets the net this pin reference is attached to (non-const version).
This constness variant has been introduced in version 0.26.8
"""
...
@overload
def net(self) -> Net:
r"""
@brief Gets the net this pin reference is attached to (non-const version).
This constness variant has been introduced in version 0.26.8
@brief Gets the net this pin reference is attached to.
"""
...
def pin(self) -> Pin:
@ -41665,17 +41701,17 @@ class NetTerminalRef:
@overload
def device(self) -> Device:
r"""
@brief Gets the device reference (non-const version).
@brief Gets the device reference.
Gets the device object that this connection is made to.
This constness variant has been introduced in version 0.26.8
"""
...
@overload
def device(self) -> Device:
r"""
@brief Gets the device reference.
@brief Gets the device reference (non-const version).
Gets the device object that this connection is made to.
This constness variant has been introduced in version 0.26.8
"""
...
def device_class(self) -> DeviceClass:
@ -42687,17 +42723,26 @@ class Netlist:
@overload
def circuit_by_name(self, name: str) -> Circuit:
r"""
@brief Gets the circuit object for a given name.
@brief Gets the circuit object for a given name (const version).
If the name is not a valid circuit name, nil is returned.
This constness variant has been introduced in version 0.26.8.
"""
...
@overload
def circuit_by_name(self, name: str) -> Circuit:
r"""
@brief Gets the circuit object for a given name (const version).
@brief Gets the circuit object for a given name.
If the name is not a valid circuit name, nil is returned.
"""
...
@overload
def circuits_by_name(self, name_pattern: str) -> List[Circuit]:
r"""
@brief Gets the circuit objects for a given name filter.
The name filter is a glob pattern. This method will return all \Circuit objects matching the glob pattern.
This constness variant has been introduced in version 0.26.8.
This method has been introduced in version 0.26.4.
"""
...
@overload
@ -42710,15 +42755,6 @@ class Netlist:
This constness variant has been introduced in version 0.26.8.
"""
...
@overload
def circuits_by_name(self, name_pattern: str) -> List[Circuit]:
r"""
@brief Gets the circuit objects for a given name filter.
The name filter is a glob pattern. This method will return all \Circuit objects matching the glob pattern.
This method has been introduced in version 0.26.4.
"""
...
def combine_devices(self) -> None:
r"""
@brief Combines devices where possible
@ -60838,17 +60874,17 @@ class SubCircuit(NetlistObject):
@overload
def circuit(self) -> Circuit:
r"""
@brief Gets the circuit the subcircuit lives in.
@brief Gets the circuit the subcircuit lives in (non-const version).
This is NOT the circuit which is referenced. For getting the circuit that the subcircuit references, use \circuit_ref.
This constness variant has been introduced in version 0.26.8
"""
...
@overload
def circuit(self) -> Circuit:
r"""
@brief Gets the circuit the subcircuit lives in (non-const version).
@brief Gets the circuit the subcircuit lives in.
This is NOT the circuit which is referenced. For getting the circuit that the subcircuit references, use \circuit_ref.
This constness variant has been introduced in version 0.26.8
"""
...
@overload

View File

@ -1673,6 +1673,16 @@ class Logger:
"""
...
@classmethod
def set_verbosity(cls, v: int) -> None:
r"""
@brief Sets the verbosity level for the application
See \verbosity for a definition of the verbosity levels. Please note that this method changes the verbosity level for the whole application.
The 'set_verbosity' alias has been introduced in version 0.29.11 as class attributes are not always available in Python.
"""
...
@classmethod
def warn(cls, msg: str) -> None:
r"""
@brief Writes the given string to the warning channel

View File

@ -392,34 +392,42 @@ WarningChannel::~WarningChannel ()
void
WarningChannel::puts (const char *s)
{
fputs (s, stdout);
if (verbosity () >= 0) {
fputs (s, stdout);
}
}
void
WarningChannel::endl ()
{
fputs ("\n", stdout);
m_new_line = true;
if (verbosity () >= 0) {
fputs ("\n", stdout);
m_new_line = true;
}
}
void
WarningChannel::end ()
{
if (m_colorized) {
fputs (ANSI_RESET, stdout);
if (verbosity () >= 0) {
if (m_colorized) {
fputs (ANSI_RESET, stdout);
}
fflush (stdout);
}
fflush (stdout);
}
void
WarningChannel::begin ()
{
if (m_colorized) {
fputs (ANSI_BLUE, stdout);
}
if (m_new_line) {
fputs ("Warning: ", stdout);
m_new_line = false;
if (verbosity () >= 0) {
if (m_colorized) {
fputs (ANSI_BLUE, stdout);
}
if (m_new_line) {
fputs ("Warning: ", stdout);
m_new_line = false;
}
}
}
@ -463,34 +471,42 @@ ErrorChannel::~ErrorChannel ()
void
ErrorChannel::puts (const char *s)
{
fputs (s, stderr);
if (verbosity () >= -10) {
fputs (s, stderr);
}
}
void
ErrorChannel::endl ()
{
fputs ("\n", stderr);
m_new_line = true;
if (verbosity () >= -10) {
fputs ("\n", stderr);
m_new_line = true;
}
}
void
ErrorChannel::end ()
{
if (m_colorized) {
fputs (ANSI_RESET, stderr);
if (verbosity () >= -10) {
if (m_colorized) {
fputs (ANSI_RESET, stderr);
}
fflush (stderr);
}
fflush (stderr);
}
void
ErrorChannel::begin ()
{
if (m_colorized) {
fputs (ANSI_RED, stderr);
}
if (m_new_line) {
fputs ("ERROR: ", stderr);
m_new_line = false;
if (verbosity () >= -10) {
if (m_colorized) {
fputs (ANSI_RED, stderr);
}
if (m_new_line) {
fputs ("ERROR: ", stderr);
m_new_line = false;
}
}
}

BIN
testdata/net_tracer/t10.oas.gz vendored Normal file

Binary file not shown.

BIN
testdata/net_tracer/t10_net.oas.gz vendored Normal file

Binary file not shown.

View File

@ -149,6 +149,15 @@ class BasicTest(unittest.TestCase):
def test_00(self):
# does not work with all Python versions
# (debugging shows that Python calls the setter on the metaclass,
# not on the class itself at least on 3.12)
# # static (class) properties
# pya.A.sp_i = 17
# self.assertEqual(pya.A.sp_i, 18)
# pya.A.sp_i = -1
# self.assertEqual(pya.A.sp_i, 0)
# all references of PA are released now:
ic0 = pya.A.instance_count()

View File

@ -24,6 +24,12 @@ class Basic_TestClass < TestBase
# for testing the ut logger:
puts "Special chars: <&>"
# static (class) properties
RBA::A.sp_i = 17
assert_equal( RBA::A.sp_i, 18 )
RBA::A.sp_i = -1
assert_equal( RBA::A.sp_i, 0 )
GC.start
# all references of A are released now:

View File

@ -124,6 +124,230 @@ class DBCellTests_TestClass < TestBase
end
# locking
def test_4
ly = RBA::Layout::new
top = ly.create_cell("TOP")
other = ly.create_cell("OTHER")
child = ly.create_cell("CHILD")
l1 = ly.layer(1, 0)
l2 = ly.layer(2, 0)
shape = top.shapes(l1).insert(RBA::Box::new(0, 0, 100, 200))
inst = top.insert(RBA::CellInstArray::new(child, RBA::Trans::new(RBA::Vector::new(100, 200))))
other.insert(RBA::CellInstArray::new(child, RBA::Trans::new(RBA::Vector::new(10, 20))))
assert_equal(top.is_locked?, false)
top.locked = false
assert_equal(top.is_locked?, false)
top.locked = true
assert_equal(top.is_locked?, true)
# rename is still possible
top.name = "TOP2"
# instances of the cell can still be created
toptop = ly.create_cell("TOPTOP")
toptop.insert(RBA::CellInstArray::new(top, RBA::Trans::new))
assert_equal(top.each_parent_inst.to_a[0].child_inst.to_s, "cell_index=0 r0 0,0")
begin
# forbidden now
top.shapes(l2).insert(RBA::Box::new(0, 0, 100, 200))
assert_equal(true, false)
rescue => ex
assert_equal(ex.to_s, "Cell 'TOP2' cannot be modified as it is locked in Shapes::insert")
end
begin
# forbidden now
shape.box = RBA::Box::new(1, 2, 101, 202)
assert_equal(true, false)
rescue => ex
assert_equal(ex.to_s, "Cell 'TOP2' cannot be modified as it is locked in Shape::box=")
end
begin
# forbidden now
shape.prop_id = 1
assert_equal(true, false)
rescue => ex
assert_equal(ex.to_s, "Cell 'TOP2' cannot be modified as it is locked in Shape::prop_id=")
end
begin
# forbidden now
shape.polygon = RBA::Polygon::new(RBA::Box::new(0, 0, 200, 100))
assert_equal(true, false)
rescue => ex
assert_equal(ex.to_s, "Cell 'TOP2' cannot be modified as it is locked in Shape::polygon=")
end
begin
# forbidden now
inst.cell_index = other.cell_index
assert_equal(true, false)
rescue => ex
assert_equal(ex.to_s, "Cell 'TOP2' cannot be modified as it is locked in Instance::cell_index=")
end
begin
# forbidden now
inst.prop_id = 1
assert_equal(true, false)
rescue => ex
assert_equal(ex.to_s, "Cell 'TOP2' cannot be modified as it is locked in Instance::prop_id=")
end
begin
# also forbidden
top.insert(RBA::CellInstArray::new(child, RBA::Trans::new))
assert_equal(true, false)
rescue => ex
assert_equal(ex.to_s, "Cell 'TOP2' cannot be modified as it is locked in Cell::insert")
end
begin
# clear is forbidding
top.clear
assert_equal(true, false)
rescue => ex
assert_equal(ex.to_s, "Cell 'TOP2' cannot be modified as it is locked in Cell::clear")
end
begin
# clear layer is forbidden
top.shapes(l1).clear
assert_equal(true, false)
rescue => ex
assert_equal(ex.to_s, "Cell 'TOP2' cannot be modified as it is locked in Shapes::clear")
end
begin
# clear layer is forbidden
top.clear(l1)
assert_equal(true, false)
rescue => ex
assert_equal(ex.to_s, "Cell 'TOP2' cannot be modified as it is locked in Cell::clear")
end
begin
# layer copy is forbidden
top.copy(l1, l2)
assert_equal(true, false)
rescue => ex
assert_equal(ex.to_s, "Cell 'TOP2' cannot be modified as it is locked in Cell::copy")
end
begin
# layer move is forbidden
top.move(l1, l2)
assert_equal(true, false)
rescue => ex
assert_equal(ex.to_s, "Cell 'TOP2' cannot be modified as it is locked in Cell::move")
end
begin
# layer swap is forbidden
top.swap(l1, l2)
assert_equal(true, false)
rescue => ex
assert_equal(ex.to_s, "Cell 'TOP2' cannot be modified as it is locked in Cell::swap")
end
begin
# copy_instances is forbidden
top.copy_instances(other)
assert_equal(true, false)
rescue => ex
assert_equal(ex.to_s, "Cell 'TOP2' cannot be modified as it is locked in Cell::copy_instances")
end
begin
# move_instances is forbidden
top.move_instances(other)
assert_equal(true, false)
rescue => ex
assert_equal(ex.to_s, "Cell 'TOP2' cannot be modified as it is locked in Cell::move_instances")
end
begin
# copy_tree is forbidden
top.copy_tree(other)
assert_equal(true, false)
rescue => ex
assert_equal(ex.to_s, "Cell 'TOP2' cannot be modified as it is locked in Cell::copy_tree")
end
begin
# move_tree is forbidden
top.move_tree(other)
assert_equal(true, false)
rescue => ex
assert_equal(ex.to_s, "Cell 'TOP2' cannot be modified as it is locked in Cell::move_tree")
end
begin
# copy_shapes is forbidden
top.copy_shapes(other)
assert_equal(true, false)
rescue => ex
assert_equal(ex.to_s, "Cell 'TOP2' cannot be modified as it is locked in Cell::copy_shapes")
end
begin
# move_shapes is forbidden
top.move_shapes(other)
assert_equal(true, false)
rescue => ex
assert_equal(ex.to_s, "Cell 'TOP2' cannot be modified as it is locked in Cell::move_shapes")
end
begin
# flatten is forbidden
top.flatten(true)
assert_equal(true, false)
rescue => ex
assert_equal(ex.to_s, "Cell 'TOP2' cannot be modified as it is locked in Cell::flatten")
end
begin
# cell cannot be deleted
top.delete
assert_equal(true, false)
rescue => ex
assert_equal(ex.to_s, "Cell 'TOP2' cannot be modified as it is locked in Cell::delete")
end
# locked attribute is copied
ly2 = ly.dup
assert_equal(ly2.cell("TOP2").is_locked?, true)
begin
# cell cannot be deleted
ly2.cell("TOP2").delete
assert_equal(true, false)
rescue => ex
assert_equal(ex.to_s, "Cell 'TOP2' cannot be modified as it is locked in Cell::delete")
end
# clear and _destroy is always possible
ly2.clear
ly2 = ly.dup
ly2._destroy
# resetting the locked flag enables things again (not all tested here)
top.locked = false
inst.cell_index = other.cell_index
top.shapes(l2).insert(RBA::Box::new(0, 0, 100, 200))
shape.box = RBA::Box::new(1, 2, 101, 202)
top.delete
end
end
load("test_epilogue.rb")