Python Dynamic DSL for data access and manipulation
Project description
PynDD (Python Dynamic DSL)
A lightweight Python library for dynamic data structure parsing and manipulation using a custom Domain Specific Language (DSL).
Installation
pip install pyndd
Quick Start
from pyndd.parser import parse, translate
# Basic usage
data = {'users': [{'name': 'Alice', 'age': 30}, {'name': 'Bob', 'age': 25}]}
names = parse('data:users:[#name]', data=data)
print(names) # ['Alice', 'Bob']
DSL Syntax Guide
Basic Structure
The DSL uses a colon-separated syntax: variable:accessor1:accessor2:...
Accessors
1. Dictionary/Object Access (#key)
data = {'user': {'name': 'Alice', 'age': 30}}
name = parse('data:#user:#name', data=data)
print(name) # 'Alice'
2. List/Array Access by Index (number)
data = {'items': ['a', 'b', 'c', 'd']}
item = parse('data:#items:1', data=data)
print(item) # 'b'
3. Slice Access ([start..end])
data = {'items': ['a', 'b', 'c', 'd', 'e']}
subset = parse('data:#items:[1..4]', data=data)
print(subset) # ['b', 'c', 'd']
# Open-ended slices
beginning = parse('data:#items:[..2]', data=data) # ['a', 'b']
ending = parse('data:#items:[2..]', data=data) # ['c', 'd', 'e']
all_items = parse('data:#items:[..]', data=data) # ['a', 'b', 'c', 'd', 'e']
4. Map Operations ([#key])
Extract specific fields from each item in a list:
data = {'users': [
{'name': 'Alice', 'age': 30},
{'name': 'Bob', 'age': 25}
]}
names = parse('data:#users:[#name]', data=data)
print(names) # ['Alice', 'Bob']
ages = parse('data:#users:[#age]', data=data)
print(ages) # [30, 25]
5. Pattern Matching (*pattern*)
Match keys using wildcards:
data = {
'user_alice': {'score': 100},
'user_bob': {'score': 85},
'admin_charlie': {'score': 95}
}
# Get all user_* entries
users = parse('data:user_*', data=data)
print(users) # {'user_alice': {'score': 100}, 'user_bob': {'score': 85}}
# Get scores from user_* entries
user_scores = parse('data:user_*:[#score]', data=data)
print(user_scores) # [100, 85]
6. Variable-based Key Access
Use variables to specify keys dynamically:
data = {'items': ['x', 'y', 'z']}
indices = [0, 2]
selected = parse('data:#items:indices', data=data, indices=indices)
print(selected) # ['x', 'z']
Complex Examples
Chaining Operations
data = {
'departments': [
{
'name': 'Engineering',
'employees': [
{'name': 'Alice', 'skills': ['Python', 'JavaScript']},
{'name': 'Bob', 'skills': ['Java', 'C++']}
]
},
{
'name': 'Marketing',
'employees': [
{'name': 'Charlie', 'skills': ['SEO', 'Content']}
]
}
]
}
# Get all employee names
all_names = parse('data:#departments:[#employees]:[#name]', data=data)
print(all_names) # [['Alice', 'Bob'], ['Charlie']]
# Get skills of first employee in each department
first_skills = parse('data:#departments:[#employees]:0:[#skills]', data=data)
print(first_skills) # [['Python', 'JavaScript'], ['SEO', 'Content']]
Nested Slicing
data = {
'matrix': [
[1, 2, 3, 4],
[5, 6, 7, 8],
[9, 10, 11, 12]
]
}
# Get middle 2x2 submatrix
submatrix = parse('data:#matrix:[1..3]:[1..3]', data=data)
print(submatrix) # [[6, 7], [10, 11]]
Data Modification with translate()
The translate() function allows you to modify data using assignment operations.
Basic Assignment
data = {'user': {'name': 'Alice'}}
translate('data:#user:#age < 30', data=data)
print(data) # {'user': {'name': 'Alice', 'age': 30}}
Bulk Assignment
data = {'users': [{'name': 'Alice'}, {'name': 'Bob'}]}
translate('data:#users:[#age] < 25', data=data)
print(data) # {'users': [{'name': 'Alice', 'age': 25}, {'name': 'Bob', 'age': 25}]}
Copy Data Between Structures
source = {'items': [1, 2, 3]}
target = {}
translate('target:#copied < source:#items', source=source, target=target)
print(target) # {'copied': [1, 2, 3]}
Advanced Features
Pattern-based Operations
config = {
'db_host': 'localhost',
'db_port': 5432,
'db_name': 'myapp',
'cache_host': 'redis-server',
'cache_port': 6379
}
# Get all database-related configs
db_config = parse('config:db_*', config=config)
print(db_config) # {'db_host': 'localhost', 'db_port': 5432, 'db_name': 'myapp'}
Identity Operation ([-])
The identity operation [-] can be used to pass through values unchanged:
data = {'items': [1, 2, 3]}
same = parse('data:#items:[-]', data=data)
print(same) # [1, 2, 3]
Error Handling
The parser will raise ValueError for malformed expressions:
try:
parse('invalid syntax here', data={})
except ValueError as e:
print(f"Parse error: {e}")
Performance Notes
- The DSL parser is lightweight and suitable for runtime data manipulation
- Complex nested operations are supported but consider performance for deeply nested structures
- Pattern matching uses Python's
fnmatchmodule internally
License
MIT License
Project details
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file pyndd-0.1.1.tar.gz.
File metadata
- Download URL: pyndd-0.1.1.tar.gz
- Upload date:
- Size: 5.4 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.1.0 CPython/3.12.9
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
c50b539e3fafa1358f14fa396b36ecd7c4d014e46b8794a6ab92c5ac4edfb4a3
|
|
| MD5 |
6474c193a1dd19526eb694cd5b00046e
|
|
| BLAKE2b-256 |
daae1a5300fb8c92cc2a8515ff98d63ef73410728dc48bbf0da0e6386d38d4e5
|
File details
Details for the file pyndd-0.1.1-py3-none-any.whl.
File metadata
- Download URL: pyndd-0.1.1-py3-none-any.whl
- Upload date:
- Size: 5.5 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.1.0 CPython/3.12.9
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
1f45f04ae37d7b84a5e43db1e5d41fec541b6fe9036e52868151156a6aaef5a3
|
|
| MD5 |
bd7f575e5c7f9475661e6b28c8cbf2c5
|
|
| BLAKE2b-256 |
4fe10aef19a8ae125a0e81e66a1636650356850ec0c0dd808949661d22000471
|