Skip to main content

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 ([[unit_tests](../Doctor/scripts/unit_tests)-])

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 fnmatch module 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

pyndd-0.1.2.tar.gz (6.1 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

pyndd-0.1.2-py3-none-any.whl (6.2 kB view details)

Uploaded Python 3

File details

Details for the file pyndd-0.1.2.tar.gz.

File metadata

  • Download URL: pyndd-0.1.2.tar.gz
  • Upload date:
  • Size: 6.1 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.1.0 CPython/3.12.9

File hashes

Hashes for pyndd-0.1.2.tar.gz
Algorithm Hash digest
SHA256 6f247281d7a064fdd0095b4d781556ed9ce89cb84a752adcd1780fc866eaa8a2
MD5 7c93dcd625a9505adb8899e4e33970a2
BLAKE2b-256 fda5af82e184300bc48053c99ea1f4e2d4ce16129bc887feca474c97792f108e

See more details on using hashes here.

File details

Details for the file pyndd-0.1.2-py3-none-any.whl.

File metadata

  • Download URL: pyndd-0.1.2-py3-none-any.whl
  • Upload date:
  • Size: 6.2 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.1.0 CPython/3.12.9

File hashes

Hashes for pyndd-0.1.2-py3-none-any.whl
Algorithm Hash digest
SHA256 1cf0f3bc4d0f1fd5e9556dcc826ab069ac09347be738549c1fc17970d46b1b83
MD5 85b2d2dbee175d2661b5f45a07b736b2
BLAKE2b-256 4fbbe892a79553ed7e1b6b0f57bc9abb4a1c228a0f0c8019219639bd48899a8c

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page