This commit is contained in:
hellerve
2017-01-17 14:49:18 +01:00
commit e8c62738a1
10 changed files with 267 additions and 0 deletions

7
.gitignore vendored Normal file
View File

@@ -0,0 +1,7 @@
__pycache__/
*.pyc
build/
*.egg-info
dist
.coverage
.cache/

11
README.rst Normal file
View File

@@ -0,0 +1,11 @@
manipulator
=============
Python data manipulation made braindead.
Installation
------------
::
pip install manipulator

2
manipulator/__init__.py Normal file
View File

@@ -0,0 +1,2 @@
from manipulator.get import get
from manipulator.update import update, set

9
manipulator/get.py Normal file
View File

@@ -0,0 +1,9 @@
from manipulator.query import treat_query
def get(data, query):
selectors = treat_query(query)
for selector in selectors:
_, data = selector(data)
return data

53
manipulator/query.py Normal file
View File

@@ -0,0 +1,53 @@
from functools import wraps
def manipulate_all(name, data):
def manipulate_internal(fn):
for elem in data:
k, v = select_id(name, elem)
elem[k] = fn(v)
return data
return manipulate_internal
def curry(fn):
@wraps(fn)
def wrapped(x):
return lambda y: fn(x, y)
return wrapped
def select_id(name, data):
if isinstance(data, list):
name = int(name)
return name, data[name]
def select_class(name, data):
if isinstance(data, list):
return manipulate_all(name, data), [select_id(name, elem)[1] for elem in data]
return name, data[name]
def lookup_selector(start):
if start == "#":
return curry(select_id)
elif start == ".":
return curry(select_class)
raise ValueError("Unknown selector: {}".format(start))
def treat_query(query):
selectors = []
for elem in query.split(" "):
elem = elem.strip()
if not elem:
continue
selector = lookup_selector(elem[0])
selectors.append(selector(elem[1:]))
return selectors

34
manipulator/update.py Normal file
View File

@@ -0,0 +1,34 @@
import copy
from manipulator.query import treat_query
def update(inpt, query, fn, in_place=True):
if in_place:
data = inpt
else:
data = copy.deepcopy(inpt)
selectors = treat_query(query)
if not selectors:
return data
d = data
last_key = None
last_d = data
for selector in selectors:
last_d = d
last_key, d = selector(d)
if callable(last_key):
last_d = last_key(fn)
elif isinstance(d, list) or isinstance(d, dict):
d = fn(d)
else:
last_d[last_key] = fn(d)
return data
def set(inpt, query, val, in_place=True):
return update(inpt, query, lambda _: val, in_place)

21
setup.py Normal file
View File

@@ -0,0 +1,21 @@
# coding=utf-8
import os
from setuptools import find_packages, setup
with open('README.rst') as readme:
long_description = readme.read()
setup(
name = "manipulator",
version = "0.0.1",
description = "Python data manipulation made braindead",
long_description = long_description,
author = "Veit Heller",
author_email = "veit@veitheller.de",
license = "MIT License",
url = "https://github.com/hellerve/manipulator",
download_url = 'https://github.com/hellerve/hawkweed/tarball/0.0.1',
packages = find_packages(),
include_package_data = True,
)

35
test/get_test.py Normal file
View File

@@ -0,0 +1,35 @@
from manipulator import get
def test_id():
x = {}
assert get(x, "") == x
def test_simple_id_query():
x = [1]
assert get(x, "#0") == 1
def test_simple_class_query():
x = [{"k": "v"}, {"k": "v2"}]
assert get(x, ".k") == ["v", "v2"]
def test_nested_id_query():
x = [{"k": "v"}]
assert get(x, "#0 #k") == "v"
def test_nested_class_query():
x = [{"k": {"k2": "v"}}, {"k": {"k2": "v2"}}]
assert get(x, ".k .k2") == ["v", "v2"]
def test_complex_query():
x = [{"k": "v"}, {"k": {"a": [{"k": 10}, {"k": 11}]}}]
assert get(x, ".k #1 #a .k") == [10, 11]

16
test/set_test.py Normal file
View File

@@ -0,0 +1,16 @@
from manipulator import set # bad idea, overriding set constructor!
def test_complex_set():
x = [{"k": "v"}, {"k": {"a": [{"k": 10}, {"k": 11}]}}]
after = [{"k": "v"}, {"k": {"a": [{"k": 100}, {"k": 100}]}}]
assert set(x, ".k #1 #a .k", 100) == after
assert x == after
def test_complex_update_copy():
x = [{"k": "v"}, {"k": {"a": [{"k": 10}, {"k": 11}]}}]
after = [{"k": "v"}, {"k": {"a": [{"k": 100}, {"k": 100}]}}]
assert set(x, ".k #1 #a .k", 100, in_place=False) == after
assert x == [{"k": "v"}, {"k": {"a": [{"k": 10}, {"k": 11}]}}]

79
test/update_test.py Normal file
View File

@@ -0,0 +1,79 @@
from manipulator import update
def test_update_id():
x = [1]
after = [2]
assert update(x, "#0", lambda x: x+1) == [2]
assert x == after
def test_update_id_copy():
x = [1]
assert update(x, "#0", lambda x: x+1, in_place=False) == [2]
assert x == [1]
def test_update_class():
x = [{"k": 1}, {"k": 2}]
after = [{"k": 2}, {"k": 3}]
assert update(x, ".k", lambda x: x+1) == after
assert x == after
def test_update_class_copy():
x = [{"k": 1}, {"k": 2}]
after = [{"k": 2}, {"k": 3}]
assert update(x, ".k", lambda x: x+1, in_place=False) == after
assert x == [{"k": 1}, {"k": 2}]
def test_update_nested_id():
x = [{"k": 1}]
after = [{"k": 2}]
assert update(x, "#0 #k", lambda x: x+1) == after
assert x == after
def test_update_nested_id_copy():
x = [{"k": 1}]
after = [{"k": 2}]
assert update(x, "#0 #k", lambda x: x+1, in_place=False) == after
assert x == [{"k": 1}]
def test_update_nested_class():
x = [{"k": {"k2": 1}}, {"k": {"k2": 2}}]
after = [{"k": {"k2": 2}}, {"k": {"k2": 3}}]
assert update(x, ".k .k2", lambda x: x+1) == after
assert x == after
def test_update_nested_class_copy():
x = [{"k": {"k2": 1}}, {"k": {"k2": 2}}]
after = [{"k": {"k2": 2}}, {"k": {"k2": 3}}]
assert update(x, ".k .k2", lambda x: x+1, in_place=False) == after
assert x == [{"k": {"k2": 1}}, {"k": {"k2": 2}}]
def test_complex_update():
x = [{"k": "v"}, {"k": {"a": [{"k": 10}, {"k": 11}]}}]
after = [{"k": "v"}, {"k": {"a": [{"k": 11}, {"k": 12}]}}]
assert update(x, ".k #1 #a .k", lambda x: x+1) == after
assert x == after
def test_complex_update_copy():
x = [{"k": "v"}, {"k": {"a": [{"k": 10}, {"k": 11}]}}]
after = [{"k": "v"}, {"k": {"a": [{"k": 11}, {"k": 12}]}}]
assert update(x, ".k #1 #a .k", lambda x: x+1, in_place=False) == after
assert x == [{"k": "v"}, {"k": {"a": [{"k": 10}, {"k": 11}]}}]