MagicO - enabling attribute notation and JSONPath
Project description
MagicO - enabling attribute notation and JSONPath
MagicO
(Magic Object) allows you to access a dict
, list
, or tuple
Python object using the attribute notation or a JSONPath.
For example, given the following data object:
my_data = {
"a": 1,
"b": {
"c": 3,
"d": (4, 5)
},
"e": [
{"f": 6},
"xyz",
],
}
to access attribute "f", you would need to use a series of subscripts, such as my_data["e"][0]["f"])
.
As a programmer, you probably would find it more natural to use the attribute notation, such as my_data.e[0].f
, or the JSONPath notation, such as my_data["$.e[0].f"]
.
This is what MagicO
enables you to do.
To install MagicO
:
pip install magico
To use MagicO
:
from magico import MagicO
my_magic = MagicO(my_data)
Attribute notation
To access an attribute using the attribute notation:
print(my_magic) # Original data
# Output: {'a': 1, 'b': {'c': 3, 'd': (4, 5)}, 'e': [{'f': 6}, 'xyz']}
print(my_magic.e[0].f)
# Output: 6
You may create new attributes, change them, and delete them using the attribute notation.
print(my_magic) # Original data
# Output: {'a': 1, 'b': {'c': 3, 'd': (4, 5)}, 'e': [{'f': 6}, 'xyz']}
my_magic.b.g = 7
print(my_magic) # b.g is created
# Output: {'a': 1, 'b': {'c': 3, 'd': (4, 5), 'g': 7}, 'e': [{'f': 6}, 'xyz']}
my_magic.b.g = 8
print(my_magic) # b.g is updated
# Output: {'a': 1, 'b': {'c': 3, 'd': (4, 5), 'g': 8}, 'e': [{'f': 6}, 'xyz']}
del my_magic.b.g
print(my_magic) # b.g is deleted
# Output: {'a': 1, 'b': {'c': 3, 'd': (4, 5)}, 'e': [{'f': 6}, 'xyz']}
JSONPath notation
There are times when the attribute to access is programmatically formulated as a JSONPath, such as "$.e[0].f".
In this case, you may use the JSONPath as a subscript to the MagicO
object, as in the following example:
print(my_magic) # Original data
# Output: {'a': 1, 'b': {'c': 3, 'd': (4, 5)}, 'e': [{'f': 6}, 'xyz']}
print(my_magic["$.e[0].f"])
# Output: 6
# The root element of the JSONPath can be omitted
print(my_magic["e[0].f"])
# Output: 6
With the MagicO
subscript notation, you can create a "deep" attribute simply by assigning a value to it, and all missing parent attributes along the path will be created automatically. For example:
my_magic["$.b.g.h.i"] = 9 # Creating a "deep" attribute b.g.h.i
print(my_magic) # Attribute "b" is added with "g.h" to get to "i"
# Output: {'a': 1, 'b': {'c': 3, 'd': (4, 5), 'g': {'h': {'i': 9}}}, 'e': [{'f': 6}, 'xyz']}
del my_magic["$.b.g"] # Deleting the parent will delete its tree
print(my_magic) # Attribute "b.g" is deleted
# Output: {'a': 1, 'b': {'c': 3, 'd': (4, 5)}, 'e': [{'f': 6}, 'xyz']}
Data types
The data type MagicO
returns depends on how you access it:
- Attribute notation:
dict
,list
,tuple
andMagicO
objects: Returns as aMagicO
object.to_data()
: Returns the data
- Scalar (
str
,int
,bool
, etc.): Returns the data
- JSONPath notation:
- Returns the data
print("MagicO object")
print(f" {type(my_magic)}: {my_magic}") # <class 'magico.magico.MagicO'>: ...
print(f" {type(my_magic.to_data())}: {my_magic.to_data()}") # <class 'dict'>: ...
print(f" {type(my_magic.data_type())}: {my_magic.data_type()}") # <class 'type'>: <class 'dict'>
print("dict object")
print(f" {type(my_magic.e[0])}: {my_magic.e[0]}") # <class 'magico.magico.MagicO'>: {'f': 6}
print(f" {type(my_magic.e[0].to_data())}: {my_magic.e[0].to_data()}") # <class 'dict'>: {'f': 6}
print(f" {type(my_magic.e[0].data_type())}: {my_magic.e[0].data_type()}") # <class 'type'>: <class 'dict'>
print("list object")
print(f" {type(my_magic.e)}: {my_magic.e}") # <class 'magico.magico.MagicO'>: [{'f': 6}, 'xyz']
print(f" {type(my_magic.e.to_data())}: {my_magic.e.to_data()}") # <class 'list'>: [{'f': 6}, 'xyz']
print(f" {type(my_magic.e.data_type())}: {my_magic.e.data_type()}") # <class 'type'>: <class 'list'>
print("tuple object")
print(f" {type(my_magic.b.d)}: {my_magic.b.d}") # <class 'magico.magico.MagicO'>: (4, 5)
print(f" {type(my_magic.b.d.to_data())}: {my_magic.b.d.to_data()}") # <class 'tuple'>: (4, 5)
print(f" {type(my_magic.b.d.data_type())}: {my_magic.b.d.data_type()}") # <class 'type'>: <class 'tuple'>
print("Scalar")
print(f" {type(my_magic.e[0].f)}: {my_magic.e[0].f}") # <class 'int'>: 6
print("JSONPath access")
print(f" {type(my_magic['$.e[0].f'])}: {my_magic['$.e[0].f']}") # <class 'int'>: 6
print(f" {type(my_magic[''])}: {my_magic['']}") # <class 'dict'>: ...
MagicO
supports all dict
, list
, and tuple
behaviours: you may use dict methods, list methods, and tuple methods on a MagicO
object, as if it is the underlying dict
, list
, or tuple
.
For example,
# Iterable
for m in my_magic:
print(f"{m}: {my_magic[m]}")
# Output:
# a: 1
# b: {'c': 3, 'd': (4, 5)}
# e: [{'f': 6}, 'xyz']
# Sortable
my_magic.e.append([8, 6, 7, 5])
print(my_magic)
my_magic.e[-1].sort()
print(my_magic)
# Output:
# {'a': 1, 'b': {'c': 3, 'd': (4, 5)}, 'e': [{'f': 6}, 'xyz', [8, 6, 7, 5]]}
# {'a': 1, 'b': {'c': 3, 'd': (4, 5)}, 'e': [{'f': 6}, 'xyz', [5, 6, 7, 8]]}
Referential pointers
Access to a MagicO
object returns a pointer to the original data.
Updating the returned object will affect the original data object as well.
In short, MagicO
is a wrapper of the original data you created it with.
They all share the same storage.
print(my_data) # Original: {..., 'e': [{'f': 6}, 'xyz'], ...}
my_magic_data = my_magic.to_data()
# Update the data object
my_magic_data["e"][1] = "abc"
print(my_data) # Output: {..., 'e': [{'f': 6}, 'abc'], ...}
# Update the MagicO object
my_magic.e[1] = "xyz"
print(my_data) # Output: {..., 'e': [{'f': 6}, 'xyz'], ...}
Another example with JSONPath and delete. The deletion on the returned object my_magic_attr
affects the original data my_data
.
my_magic_attr = my_magic["$.e"]
print(my_magic_attr) # Output: [{'f': 6}, 'xyz', [5, 6, 7, 8]]
del my_magic_attr[-1]
print(my_data) # Output: {'a': 1, 'b': {'c': 3, 'd': (4, 5)}, 'e': [{'f': 6}, 'xyz']}
Here is a Jupyter version of this document.
If you have any questions or experience any issues, please log a MagicO ticket on GitHub.
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
File details
Details for the file magico-1.0.tar.gz
.
File metadata
- Download URL: magico-1.0.tar.gz
- Upload date:
- Size: 11.3 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/4.0.2 CPython/3.9.17
File hashes
Algorithm | Hash digest | |
---|---|---|
SHA256 | 16d4791f1280714dc1278c225d30b1dc5d4577d711be407ca3ccd0988a790d1d |
|
MD5 | 612aaa7d8daaecaa0659ff4317a8b4c6 |
|
BLAKE2b-256 | 85ec97b947f535ec99691933c0df85ab306a1ed16036272b55f5ed41a280932c |
File details
Details for the file magico-1.0-py3-none-any.whl
.
File metadata
- Download URL: magico-1.0-py3-none-any.whl
- Upload date:
- Size: 9.8 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/4.0.2 CPython/3.9.17
File hashes
Algorithm | Hash digest | |
---|---|---|
SHA256 | 990e067088bb7b34beb249dfa4a22979d84d744e9bc565935484af16f9a9e065 |
|
MD5 | b2a19a5b1f07ddc6e384a0076cc19256 |
|
BLAKE2b-256 | b5787139afba9ede78c220c1f4ea7bdee447b485cdf1cdbf6304e40c915e3463 |