pfython / cleverdict Goto Github PK
View Code? Open in Web Editor NEWA JSON-friendly data structure which allows both object attributes and dictionary keys and values to be used simultaneously and interchangeably.
License: MIT License
A JSON-friendly data structure which allows both object attributes and dictionary keys and values to be used simultaneously and interchangeably.
License: MIT License
Enable:
CleverDict({"1": "one"}')
E.g. look for string in init and attempt to convert using json.loads
CleverDict(file)
Where file is INI, YAML, TOML, JSON or .py with e.g. variable = value
lines
Add github workflow to run tests automatically on each push or PR
In Section 8, you have the following:
To deactivate .save() or .delete() separately:
>>> x.set_autosave()
>>> x.set_deleve()
At first I thought it was a typo that was supposed to be .set_delete()
, but I think it might actually be intended as .set_autodelete()
.
A nice feature enhancement to CleverDict
would be to offer a method for setting aliases directly; once the _alias
dictionary is updated (I think) the existing functionality would automatically update all aliases whenever values change, which would be fantastic.
Aliases for True/False in __str__
. Given that 1
, 1.0
, and True
are considered equivalent as dictionary keys, it would be helpfully explicit to create all possibilities as aliases regardless of which Key is given initially. Likewise False
.
>>> x = CleverDict({True: "Is this?"})
>>> print(x)
CleverDict
x[True] == x['_True'] == x._True == x[1] == x[1.0] == 'Is this?'
When a CleverDict attribute or value is changed using "=" assignment it triggers the .save()
method as intended.
list.append()
and list.extend()
do NOT trigger the autosave function however... not sure why, and I don't have any understanding of the internals, but just wondered if there it might be desirable/possible to specify a list of methods (outside of the CleverDict class) which, if called, would also trigger a mapped CleverDict method... mainly .save
and .delete
but could be other things as well.
That might be overly complicated, or it might provide another rich layer of cleverness to CleverDict. Please let me know your initial thoughts?
Currently it's possible to set dictionary items with the same (key) names as existing methods e.g.
x=CleverDict()
x['save']="What a great save!"
x['save']
'What a great save!'
They don't overwrite the existing method (a good thing!) but there is some potentially confusing behaviour:
x
CleverDict({}, _aliases={}, _vars={})
This is because CleverDict.ignore includes {'save_path', 'delete', 'save', '_aliases'}.
Furthermore, although a user is unlikely to import their own data with an existing key of "_aliases", the other three ignored attributes might quite plausibly feature. In this case the solution is just to rename the keys before importing to CleverDict but the danger is that a user might not even know/expect this to be an issue until it becomes one...
Click is used by CleverDict
solely for the purpose of locating the best (operating system specific) folder to save the autosave JSON files.
If it were possible to replicate this function (only) in CleverDict
itself, it would make CleverDict
completely dependency-free again.
In using a CleverDict instance to hold passwords (in conjunction with keyring
) I noticed the setter and delete decorators didn't appear to be working:
@property
def password(self):
return keyring.get_password(account, self.username)
@password.setter
def password(self, value):
print("Setting!")
keyring.set_password(account, self.username, value)
@password.deleter
def password(self):
keyring.delete_password(account, username)
>>> value = "mysecret"
>>> keyring.set_password(CleverSession.choices[self.url], self.username, value)
>>> self.password
'mysecret'
# set
>>> self.password = "newpassword"
>>> self.password
'mysecret'
>>> self['password']
'newpassword'
# delete
>>> del self.password
>>> self.password
'mysecret'
>>> self['password']
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "C:\Users\Peter\AppData\Roaming\Python\Python39\site-packages\cleverdict\cleverdict.py", line 168, in __getitem__
name = self.get_key(name)
File "C:\Users\Peter\AppData\Roaming\Python\Python39\site-packages\cleverdict\cleverdict.py", line 310, in get_key
raise KeyError(name)
KeyError: 'password'
So I'm just wondering if this is something that has to be worked around, a limitation of CleverDict
, or if there's something even more Clever we can do in CleverDict
itself so that @x.setter and @x.deleter work as expected right out of the box?
The .data
attribute is reserved by UserDict
and it shouldn't be possible to overwrite/break it, but it is currently, but test_data_attribute()
fails consistently. Also since data
is a relatively common Key/Attribute name it's likely to feature in people's existing dictionaries or objects. I don't know how, but wonder if we can change the (inherited) behaviour of UserDict
such that it accesses _data
rather than data
, which would also be consistent with our use of _alias
. I tried various ways to change this in both in normalise()
and in the main class, but got into a recursion tangle!
Create a new utility method (or enhance init?) to convert nested dictionaries into nested CleverDicts, so that deeper levels can be accesses easily with "." notation. For example:
>>> d = CleverDict({"level1": {"level2A": "A", "level2B": "B"}})
>>> d.level1.level2B
'B'
>>> d
CleverDict({'level1': CleverDict({'level2A': 'A', 'level2B': 'B'}, _aliases={}, _vars={})}, _aliases={}, _vars={})
I'd also like to extend this functionality to attributes, which might be trickier. For example:
>>> ac1 = AnotherClass()
>>> ac2 = AnotherClass()
>>> ac1.level2 = "Level Two"
>>> ac2.level1 = "Level One"
>>> d = CleverDict(ac1, ac2)
>>> d.ac1.level2
"Level Two"
>>> d.ac2.level1
"Level One"
And the absolute ideal would be to handle any mixture of dictionaries and objects/attributes. For example:
>>> d = CleverDict({"First Item": {"level1": {"level2A": "A", "level2B": "B"}}},
ac1, {"Second Item": {"obj": ac2}}}
>>> d.First_Item.level1.level2A
"A"
>>> d.ac1.level2
"Level Two"
>>> d.Second_Item.obj.level1
"Level One"
I was just pointed to this project via my blog article 6 Alternatives to Classes in Python. Do you happen to know pydantic? How does cleverdict compare to pydantic?
pyprojects.toml
[tool.black]
line-length = 100
target-version = ['py39']
[tool.isort]
combine_as_imports = true
line_length = 100
profile = "black"
Just as the dummy .save() method is called every time a CleverDict values changes, how about a dummy .delete() method for when a value is deleted?
This came to light when using CleverDict to auto-save to a JSON config file... updates were happening beautifully behind the scenes but when an value was deleted, it remained in the config file, meaning that the next time the script was run and the default values were loaded from the config file, the old unwanted value was still there.
Instead of a notional save in the autosave example of README, include real working version that creates a config file in the "recommended" folder (depending on operating system, using click.get_app_dir() method).
It would be lovely if cleverdict was available for anaconda/miniconda through the conda-forge channel. Should be simple to set up, since you have no dependencies and it's already on PyPi: https://conda-forge.org/#add_recipe
Thanks for the nice package!
.delete() currently just takes a key/attribute name argument which means the actual value will be deleted before any custom delete function is called. It may be important to have that value available (e.g. backup?)
Alternatively ensure custom delete (and save) are called BEFORE delitem etc.
Is it possible/desirable to override curly brackets e.g.
from cleverdict import CleverDict
x = {"a": "First letter"}
... would result in x being a CleverDict object not a regular dict?
It would be great if CleverDict
behaviour could be easily 'grafted on' to existing classes using inheritance, without causing recursion or requiring a rewrite/overwrite of the original class.
For example if it were as easy as:
```
>>> class MyDatetime(datetime.datetime, CleverDict):
... pass
>>> mdt = MyDatetime.now()
>>> mdt.hour
4
>>> mdt['hour']
4
```
A declarative, efficient, and flexible JavaScript library for building user interfaces.
๐ Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. ๐๐๐
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google โค๏ธ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.