liberty-parser
A pure-Python parser for Liberty (.lib) cell-library files — the
industry-standard format used by EDA tools to describe the timing, power,
and interface of standard cells.
Features
- Full Liberty grammar — comments, simple attributes, complex attributes, and arbitrarily deep group blocks
- Round-trip write —
write_liberty()reconstructs the file with the exact original whitespace and formatting - In-place editing —
set_val(),set_vals(),set_LU_matrix(),add_simple(),add_complex(),add_block(),remove_block(),merge_blocks() - Hierarchical navigation —
navigate()with exact-match or{RE}regexfilters,navigate_to_base()to walk up the tree - Convenience helpers —
get_cell_list(),get_pin_list(),map_LU_indexes() - Parse from string — no file required; pass a Liberty text block directly
- Zero required dependencies — only the Python standard library
- Optional colored output via
termcolor - CLI included:
liberty-parser cells.lib --cell INV_X1 --pins
Installation
pip install eda-liberty-parser # no extra dependencies
pip install "eda-liberty-parser[color]" # adds termcolor for colored output
Or for development:
git clone https://github.com/rohaansch/liberty-parser
cd liberty-parser
pip install -e ".[dev]"
Quick start
from liberty_parser import LP
lib = LP("cells.lib")
print(lib.get_cell_list())
# ['INV_X1', 'AND2_X1', 'DFFS_X2']
print(lib.get_pin_list(cell="INV_X1"))
# ['A', 'ZN']
# Read a library attribute
time_unit = lib.navigate_single('library', 'time_unit').get_val()
print(time_unit)
# 1ns
Navigation
navigate() accepts a chain of filter strings, one per hierarchy level:
# All cells in the library
cells = lib.navigate('library', 'cell')
# Specific cell by name
inv = lib.navigate_single('library', 'cell:INV_X1')
# Pin direction of a specific pin
direction = lib.navigate_single('library', 'cell:INV_X1', 'pin:ZN', 'direction')
print(direction.get_val())
# output
# Regex filter — find all *_template blocks
for tmpl in lib.navigate('library', '{RE}_template$'):
print(tmpl.get_val())
navigate_single() returns the first match (or None).
navigate_singles() is the fastest variant — stops at the first match at
every level.
In-place editing
All edits modify the in-memory dict and are written back with write_liberty():
# Change a simple attribute value
lib.navigate_single('library', 'time_unit').set_val('2ns')
# Or use the two-argument shorthand (nav_to, new_value)
lib.navigate_single('library').set_val('voltage_unit', '1.8V')
# Edit a lookup-table 2-D matrix
cell_rise = lib.navigate_single(
'library', 'cell:INV_X1', 'pin:ZN', 'timing', 'cell_rise'
)
matrix = cell_rise.get_LU_matrix()
matrix[0][0] = '0.15'
cell_rise.set_LU_matrix(matrix)
# Write back to disk
lib.write_liberty("cells_modified.lib")
Structural edits
library = lib.navigate_single('library')
# Add a new simple attribute
library.add_simple('nom_voltage', '1.8')
# Add a new complex attribute
library.add_complex('operating_conditions', 'typical')
# Remove a cell
cell = lib.navigate_single('library', 'cell:INV_X1')
cell.remove_block()
# Merge another Liberty block into an existing one
lib.navigate_single('library').merge_blocks('extra_cells.lib')
Parsing from a string
No file needed — pass Liberty text directly:
snippet = 'library (mini) { voltage_unit : "1V"; nom_voltage : 1.8; }'
mini = LP(snippet)
print(mini.navigate_single('library', 'nom_voltage').get_val())
# 1.8
Element type predicates
from liberty_parser import is_comment, is_simple_attribute, is_complex_attribute, is_block
for element in lib.navigate('library'):
if is_block(element):
print(f"Block: {element.get_val()}")
elif is_simple_attribute(element):
print(f"Attr: {element.get_val()}")
API reference
LP(arg=None)
| Argument | Behavior |
|---|---|
None |
Create empty LP object |
filepath (.lib file) |
Parse the file |
text (Liberty string) |
Parse the string as a Liberty block |
dict |
Wrap an existing internal dict |
LP |
Share the internal dict of another LP object |
Navigation
| Method | Description |
|---|---|
navigate(*filters) |
Return all matching LP objects |
navigate_single(*filters) |
Return first match at last filter |
navigate_singles(*filters) |
Return first match at every filter (fastest) |
navigate_to_base() |
Walk up to the file root |
Getters
| Method | Description |
|---|---|
get_val(nav_to=None) |
Return value or name string |
get_vals(nav_to=None) |
Return comma-separated value as a list |
get_LU_matrix(nav_to=None) |
Return LU table as a 2-D list |
get_type() |
Return the element type string |
get_cell_list() |
Return all cell names |
get_pin_list(cell=None) |
Return pin names for one or all cells |
map_LU_indexes() |
Return transition/load index map |
Setters
| Method | Description |
|---|---|
set_val(nav_or_val, val=None) |
Set a simple or complex attribute value |
set_vals(nav_or_vals, vals=None) |
Replace comma-separated value list |
set_LU_matrix(nav_or_matrix, matrix=None) |
Replace LU table 2-D matrix |
Structural edits
| Method | Description |
|---|---|
add_simple(name, value, insert_ndx=0) |
Insert a simple attribute |
add_complex(type, name, insert_ndx=0) |
Insert a complex attribute |
add_block(lp_block, insert_ndx=0) |
Insert an LP element |
add_blocks(input, insert_ndx=0) |
Parse and insert multiple elements |
remove_block(ndx=None) |
Remove element at index, or remove self |
move_block(from_index, to_index) |
Reorder elements |
merge_blocks(blocks_input) |
Deep merge another Liberty block |
Write & debug
| Method | Description |
|---|---|
write_liberty(out_path) |
Write parsed dict back to a .lib file |
print(indent=0) |
Pretty-print the internal dict |
Command-line usage
liberty-parser cells.lib
liberty-parser cells.lib --cells
liberty-parser cells.lib --cell INV_X1
liberty-parser cells.lib --cell INV_X1 --pins
liberty-parser cells.lib --attr voltage_unit
liberty-parser --version
Or without installing:
python -m liberty_parser cells.lib --cell INV_X1 --pins
Running the tests
pip install -e ".[dev]"
pytest -v
Companion projects
- sdf-parser — Standard Delay Format
- verilog-parser — Verilog cell-library interfaces
License
Metadata
Release files for eda-liberty-parser 0.1.1
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | Provenance | |
|---|---|---|---|---|
| eda_liberty_parser-0.1.1.tar.gz | 18.6 kB | Without provenance | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| eda_liberty_parser-0.1.1-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 36.2 kB
Release files / eda_liberty_parser-0.1.1.tar.gz
| Download URL | eda_liberty_parser-0.1.1.tar.gz |
|---|---|
| Size | 18.6 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
c7bdae6cee2993a8564e080d1fba68b8cd3a9cef014789f5d0f452c60c45e275
|
|
BLAKE2b-256 checksum How to use checksums |
58edc99e718cc21ea3da76244647dbf47ca28b746a35f4049558a85a89d0fb0e
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/6.2.0 CPython/3.14.3
|
Release files / eda_liberty_parser-0.1.1-py3-none-any.whl
| Download URL | eda_liberty_parser-0.1.1-py3-none-any.whl |
|---|---|
| Size | 17.5 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
6433d1c939fbd73b70c92e4d4dab881e78eed87ecd072cacf27de78df025367a
|
|
BLAKE2b-256 checksum How to use checksums |
d29881ca0d0253bc8481ee2eeeef06f8031bd1ed7c50eb1371ef73f94d5a0e29
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/6.2.0 CPython/3.14.3
|
Security for eda-liberty-parser 0.1.1
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
No provenance found
None of the files in this release were uploaded with provenance metadata.
A note on package security
PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.
To learn more about protecting yourself from malicious actors, visit our package security documentation.