🚀 Version 3.0 - Modern Python library (3.8+) with uv support! See docs/ for guides.
Show configuration parser (shconfparser) is a Python library for parsing network device configurations. This library examines the config and breaks it into a set of parent and clild relationships.
shconfparser is a vendor independent library where you can parse the following formats:
- Tree structure
i.e. show running - Table structure
i.e. show ip interface - Data
i.e. show version
YAML Format Output
Tree Structure
Table Structure
✨ Zero Dependencies - Uses only Python standard library
⚡ Fast - Modern tooling with uv package manager support
🔒 Type Safe - Full type hints and py.typed marker
🎯 Vendor Independent - Works with any network device configuration
📊 Multiple Formats - Parse trees, tables, and unstructured data
📄 Format Flexibility - Output as JSON or YAML structures
🔍 XPath Queries - NSO-style queries with context tracking (NEW!)
🧪 Well Tested - 80%+ code coverage, tested on Python 3.8-3.13
pip install shconfparserFaster with uv:
curl -LsSf https://astral.sh/uv/install.sh | sh
uv pip install shconfparserSingle show command with YAML format (recommended):
from shconfparser.parser import Parser
# Use YAML format for cleaner output and XPath support
p = Parser(output_format='yaml')
data = p.read('running_config.txt')
# Parse directly (no split needed for single show running command)
tree = p.parse_tree(data)
print(p.dump(tree, indent=2))
# Query with XPath
result = p.xpath('/hostname')
print(result.data) # 'R1'Alternative: JSON format (backward compatible)
p = Parser() # Default is JSON format (OrderedDict)
data = p.read('running_config.txt')
tree = p.parse_tree(data)
print(p.dump(tree, indent=4))Multiple show commands in one file:
from shconfparser.parser import Parser
p = Parser(output_format='yaml') # YAML format recommended
data = p.read('multiple_commands.txt') # Contains multiple show outputs
data = p.split(data) # Split into separate commands
data.keys()
# odict_keys(['running', 'version', 'cdp_neighbors', 'ip_interface_brief'])
# Now parse each command separately
data['running'] = p.parse_tree(data['running'])
headers = ['Device ID', 'Local Intrfce', 'Holdtme', 'Capability', 'Platform', 'Port ID']
data['cdp_neighbors'] = p.parse_table(data['cdp_neighbors'], header_names=headers)
print(p.dump(data['running'], indent=2))Alternative: Access internal properties
p = Parser()
p.read('multiple_commands.txt')
p.split(p.r.data)
# Access split data from internal property
data = p.s.shcmd_dict
data['running'] = p.parse_tree(data['running'])
print(p.dump(data['running'], indent=4))import shconfparser
print(shconfparser.__version__) # '3.0.0'from shconfparser.parser import Parser
p = Parser()
# Single command file - parse directly
data = p.read('running_config.txt')
tree = p.parse_tree(data) # No split() needed
# Access nested configuration
print(p.dump(tree['interface FastEthernet0/0'], indent=2))
# {
# "ip address 1.1.1.1 255.255.255.0": null,
# "duplex auto": null,
# "speed auto": null
# }# Single command file
p = Parser()
data = p.read('cdp_neighbors.txt')
# Parse table directly (no split needed)
headers = ['Device ID', 'Local Intrfce', 'Holdtme', 'Capability', 'Platform', 'Port ID']
cdp_data = p.parse_table(data, header_names=headers)
# Access as list of dictionaries
for neighbor in cdp_data:
print(f"{neighbor['Device ID']} on {neighbor['Local Intrfce']}")
# Output: R2 on Fas 0/0# Single command file
p = Parser()
data = p.read('show_version.txt')
# Parse show version output directly
version_data = p.parse_data(data) # No split() needed
# Search for specific information
import re
for line in version_data.keys():
if re.search(r'IOS.*Version', line):
print(line)
# Output: Cisco IOS Software, 3700 Software (C3725-ADVENTERPRISEK9-M), Version 12.4(25d)...# Search for all interfaces
pattern = r'interface\s+\w+.*'
matches = p.search.search_all_in_tree(pattern, tree)
for key, value in matches.items():
print(value)
# interface FastEthernet0/0
# interface FastEthernet0/1# Find specific device in CDP table
pattern = r'R\d+'
match = p.search.search_in_table(pattern, cdp_data, 'Device ID')
print(match)
# {'Device ID': 'R2', 'Local Intrfce': 'Fas 0/0', ...}Parse configurations to JSON (OrderedDict) or YAML-friendly dict structures:
from shconfparser.parser import Parser
# Default: JSON format (OrderedDict - backward compatible)
p = Parser()
data = p.read('running_config.txt')
tree = p.parse_tree(data) # Returns OrderedDict
print(type(tree)) # <class 'collections.OrderedDict'>
# YAML format: cleaner hierarchical structure
p = Parser(output_format='yaml')
data = p.read('running_config.txt')
tree_yaml = p.parse_tree(data) # Returns dict with nested structure
print(type(tree_yaml)) # <class 'dict'>
# Override format per call
p = Parser() # Default is JSON
tree_json = p.parse_tree(data) # OrderedDict
tree_yaml = p.parse_tree(data, format='yaml') # dict
# YAML structure example:
# Input: "interface FastEthernet0/0" with nested config
# JSON: {"interface FastEthernet0/0": {...}}
# YAML: {"interface": {"FastEthernet0/0": {...}}}Format Comparison:
# JSON format (default) - preserves exact CLI structure
{
"interface FastEthernet0/0": {
"ip address 1.1.1.1 255.255.255.0": "",
"duplex auto": ""
}
}
# YAML format - hierarchical and human-readable
{
"interface": {
"FastEthernet0/0": {
"ip": {
"address": "1.1.1.1 255.255.255.0"
},
"duplex": "auto"
}
}
}Benefits of YAML format:
- Cleaner hierarchy for nested configurations
- Better for programmatic access
- Easier to convert to actual YAML files
- Natural structure for complex configs
- Required for XPath queries
Query YAML-formatted configurations using NSO-style XPath with optional context tracking:
from shconfparser.parser import Parser
# XPath requires YAML format
p = Parser(output_format='yaml')
data = p.read('running_config.txt')
tree = p.parse_tree(data)
# Simple queries
result = p.xpath('/hostname')
print(result.data) # 'R1'
# Wildcards - find all interface duplex settings
result = p.xpath('/interface/*/duplex')
print(result.matches) # ['auto', 'auto']
print(result.count) # 2
# Predicates with slashes (network interface names)
result = p.xpath('/interface[FastEthernet0/0]/duplex')
print(result.data) # 'auto'
# Recursive search - find anywhere in tree
result = p.xpath('//duplex')
print(result.matches) # ['auto', 'auto']
# Predicate wildcards
result = p.xpath('/interface[FastEthernet*]/ip/address')
print(result.data) # '1.1.1.1 255.255.255.0'Context Options - Solve the "which interface?" problem:
# Problem: Can't identify source with wildcards
result = p.xpath('/interface/*/duplex')
print(result.matches) # ['auto', 'auto'] - Which interface?
# Solution 1: context='none' (default - just values)
result = p.xpath('/interface/*/duplex', context='none')
print(result.matches) # ['auto', 'auto']
# Solution 2: context='partial' (from wildcard match point)
result = p.xpath('/interface/*/duplex', context='partial')
print(result.matches)
# [{'FastEthernet0/0': {'duplex': 'auto'}},
# {'FastEthernet0/1': {'duplex': 'auto'}}]
# Solution 3: context='full' (complete tree hierarchy)
result = p.xpath('/interface/*/duplex', context='full')
print(result.matches)
# [{'interface': {'FastEthernet0/0': {'duplex': 'auto'}}},
# {'interface': {'FastEthernet0/1': {'duplex': 'auto'}}}]
# Path tracking (always available)
result = p.xpath('/interface/*/speed')
print(result.paths)
# [['interface', 'FastEthernet0/0', 'speed'],
# ['interface', 'FastEthernet0/1', 'speed']]XPath Features:
- ✅ Absolute paths:
/interface/FastEthernet0/0/duplex - ✅ Recursive search:
//duplex(find anywhere) - ✅ Wildcards:
/interface/*/duplex - ✅ Predicates:
/interface[FastEthernet0/0] - ✅ Predicate wildcards:
/interface[FastEthernet*] - ✅ Context tracking: See which match came from where
- ✅ Path tracking:
result.pathsshows path components
XPathResult Structure:
result = p.xpath('//duplex')
print(result.success) # True
print(result.data) # First match: 'auto'
print(result.matches) # All matches: ['auto', 'auto']
print(result.count) # Number of matches: 2
print(result.query) # Original query: '//duplex'
print(result.paths) # Path to each match
print(result.error) # Error message if failed
# Boolean check
if result:
print(f"Found {result.count} matches")Note: XPath queries only work with output_format='yaml'. JSON format (OrderedDict) preserves exact CLI structure and should use traditional dict navigation.
For advanced users who need granular control
from shconfparser import Reader, ShowSplit, TreeParser, TableParser
# For multiple show commands
reader = Reader('multiple_commands.txt')
splitter = ShowSplit()
data = splitter.split(reader.data) # Split only if multiple commands
# Use specific parsers
tree_parser = TreeParser()
table_parser = TableParser()
running = tree_parser.parse(data['running'])
cdp = table_parser.parse(data['cdp_neighbors'], header_names=headers)💡 Remember: Use split() only when your file contains multiple show commands. For single command files, parse directly.
📖 For more examples, see docs/ folder.
📚 Complete documentation: docs/README.md
| Guide | Description |
|---|---|
| Usage Examples | Detailed parsing examples (tree, table, data) |
| API Reference | Complete API documentation |
| Migration Guide | Upgrade from v2.x to v3.0 |
| Python Compatibility | Python version support |
| Guide | Description |
|---|---|
| Quick Start | 5-minute contributor setup |
| Contributing Guide | How to contribute |
| Architecture | System design and structure |
| Business Standards | Quality and compliance standards |
- 📖 Documentation: docs/README.md
- 🐛 Bug Reports: GitHub Issues
- 💬 Questions: Stack Overflow (tag:
shconfparser) - 📧 Email: kirankotari@live.com
Q: What Python versions are supported?
A: Python 3.8-3.13 are fully tested and supported.
Q: Does this work with my network vendor?
A: Yes! shconfparser is vendor-independent and works with any hierarchical configuration format.
Q: Are there any dependencies?
A: No runtime dependencies - uses only Python standard library.
Q: How do I migrate from v2.x?
A: The API is backward compatible. Just run pip install --upgrade shconfparser. See Migration Guide for details.
- 🌟 Star us on GitHub
- 🤝 Contribute: See CONTRIBUTING.md
- 📊 CI/CD: Automated testing on Python 3.8-3.13 across Ubuntu, macOS, Windows
MIT License © 2016-2025 Kiran Kumar Kotari
Special thanks to all contributors


