Skip to main content

Dotsi

Dot-accessible, update-aware Python dicts (& lists). Works recursively, like a charm.

Dotsi defines two classes, dotsi.Dict and dotsi.List, which work together to bring JavaScript-like dot-notation to Python dicts (and lists therein).

Installation

pip install dotsi

Alternately, download dotsi.py it into your project directory.

Usage

Let's dive right in:

>>> import dotsi
>>> 
>>> d = dotsi.Dict({"foo": {"bar": "baz"}})     # Basic
>>> d.foo.bar
'baz'
>>> d.users = [{"id": 0, "name": "Alice"}]   # List
>>> d.users[0].name
'Alice'
>>> d.users.append({"id": 1, "name": "Becca"}); # Append
>>> d.users[1].name
'Becca'
>>> d.users += [{"id": 2, "name": "Cathy"}];    # `+=`
>>> d.users[2].name
'Cathy'
>>> d.update({"tasks": [{"id": "a", "text": "Task A"}]});
>>> d.tasks[0].text
'Task A'
>>> d.tasks[0].tags = ["red", "white", "blue"];
>>> d.tasks[0].tags[2];
'blue'
>>> d.tasks[0].pop("tags")                      # `.pop()`
['red', 'white', 'blue']
>>> 
>>> import pprint
>>> pprint.pprint(d)
{'foo': {'bar': 'baz'},
 'tasks': [{'id': 'a', 'text': 'Task A'}],
 'users': [{'id': 0, 'name': 'Alice'},
           {'id': 1, 'name': 'Becca'},
           {'id': 2, 'name': 'Cathy'}]}
>>> 
>>> type(d.users)       # dotsi.Dict (AKA dotsi.DotsiDict)
<class 'dotsi.DotsiList'>
>>> type(d.users[0])    # dotsi.List (AKA dotsi.DotsiList)
<class 'dotsi.DotsiDict'> 
>>> 

In the above example, while we explicitly initialized d as an dotsi.Dict:

  • d.users automatically became a dotsi.List.
  • d.users[0] automatically became a dotsi.Dict.

Dotsi vs Others

Addict:

At Polydojo, we've been using Addict for quite some time. It's a great library! But it doesn't play well with list-nested (inner) dicts.

>>> import addict
>>> 
>>> d = addict.Dict({"foo": {"bar": "baz"}})
>>> d.foo
{'bar': 'baz'}
>>> d.users = [{"id": 0, "name": "Alice"}]
>>> d.users[0].name
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: 'dict' object has no attribute 'name'
>>> 

EasyDict:

EasyDict is another great library. It works recursively, but doesn't fully support list-nested dict updates.

>>> import easydict
>>> 
>>> d = easydict.EasyDict({"foo": {"bar": "baz"}})
>>> d.foo
{'bar': 'baz'}
>>> d.users = [{"id": 0, "name": "Alice"}]
>>> d.users[0].name
'Alice'
>>> d.users.append({"id": 1, "name": "Becca"});
>>> d.users[1].name
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: 'dict' object has no attribute 'name'
>>> 

Shortcuts

Classes:

  • dotsi.Dict is a short alias for dotsi.DotsiDict.
  • dotsi.List is a short alias for dotsi.DotsiList.

Functions:

  • dotsi.dotsify() calls dotsi.Dict/dotsi.List, as appropriate.
  • dotsi.fy() is a short alias for dotsi.dotsify().
  • dotsi.mapdotsify() is like the built-in map(), but returns a dotsi.List.
  • dotsi.mapfy is a short alias for dotsi.mapdotsify(). (More on this below.)

In most cases, all you need is:

  • dotsi.fy(thing), where thing is a dict or list.

Dict-Like Objects

While dotsi.fy() converts objects of type dict to dotsi.Dict, it doesn't touch other dict-like objects, such as those of type collections.OrderedDict or http.cookies.SimpleCookie.

To convert a non-dict, but dict-like object to dotsi.Dict, use dotsi.Dict(.) directly, or use dotsi.fy(dict(.)).

>>> import dotsi
>>> from collections import OrderedDict
>>> 
>>> d = OrderedDict({"foo": {"bar": "baz"}})
>>> d
OrderedDict([('foo', {'bar': 'baz'})])
>>> type(d)
<class 'collections.OrderedDict'>
>>>
>>> x = dotsi.fy(d)
>>> x
OrderedDict([('foo', {'bar': 'baz'})])
>>> type(x)
<class 'collections.OrderedDict'>
>>> 
>>> y = dotsi.Dict(d)
>>> y
{'foo': {'bar': 'baz'}}
>>> type(y)
<class 'dotsi.DotsiDict'>
>>> 
>>> z = dotsi.fy(dict(d))
>>> z
{'foo': {'bar': 'baz'}}
>>> type(z)
<class 'dotsi.DotsiDict'>

Subclasses of dict, such as http.cookie.SimpleCookie, often implement custom behavior, which would be lost on conversion to dotsi.Dict. Thus, automatic conversion shouldn't be implemented.

Quick Plug

Dotsi is built and maintained by the folks at Polydojo, Inc., led by Sumukh Barve. If your team is looking for a simple project management tool, please check out our latest product: BoardBell.com.

List-Like Objects

Like with dicts, dotsi.fy(.) only converts objects of type list to dotsi.List, but doesn't touch other list-like objects or tuples. To convert a non-list, but list-like object to dotsi.List, directly call dotsi.List(.) or use dotsi.fy(list(.))

Identity Function

For non-dict and non-list objects, dotsi.fy(.) is equivalent to the identity function.

Kindly note that from Python3+, the built-in map() produces a non-list iterable. Thus, calling dotsi.fy(map(.)) is equivalent to just map(.). Instead, please use dotsi.List(map(.)).

Mapping Helper

As mapping is a pretty-common use case, we've included dotsi.mapfy(.), which is essentially equivalent to dotsi.List(map(.)). But additionally, with dotsi.mapfy(.), for mapping onto a single sequence, you may pass arguments in either order.

That is, the following lines are equivalent:

  • x = dotsi.mapfy(lambda n: {"n": n}, [0, 1, 2])
  • x = dotsi.mapfy([0, 1, 2], lambda n: {"n": n})

In either case, x[0].n == 0 will be True.

When mapping onto multiple sequences, dotsi.mapfy(.) expects the same order of arguments as map(.).

Overridden Methods

Excluding magic-methods like .__init__(.) etc., methods overridden by Dotsi are listed below.

dotsi.Dict overrides:

  • .update(.)
  • .setdefault(.)
  • .copy(.)

dotsi.List overrides:

  • insert(.)
  • append(.)
  • extend(.)
  • copy(.)

Signatures for all overridden methods should be equivalent (if not exactly identical) to their non-overridden counterparts.

Licensing

Copyright (c) 2020 Polydojo, Inc.

Software Licensing:
The software is released "AS IS" under the MIT license, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED. Kindly see LICENSE.txt for more details.

No Trademark Rights:
The above software licensing terms do not grant any right in the trademarks, service marks, brand names or logos of Polydojo, Inc.

Metadata

Release files for dotsi 0.0.3

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for dotsi 0.0.3
File Size Uploaded
dotsi-0.0.3.tar.gz 6.8 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for dotsi 0.0.3
File Interpreter ABI Platform
dotsi-0.0.3-py2.py3-none-any.whl Python 3, Python 2 none any Details

Total release size: 12.9 kB

Release files / dotsi-0.0.3.tar.gz

Download URL dotsi-0.0.3.tar.gz
Size 6.8 kB
Tags Source
SHA-256 checksum
How to use checksums
70209fb30b254b625517fcb9022c48b3ba1d91f887fcc56cac98214c16a40cae
BLAKE2b-256 checksum
How to use checksums
80f34fe653038b8e0cfd4dda9e4658173f954bf66f97a5db5d7039c737456f9e
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via python-requests/2.24.0

Release files / dotsi-0.0.3-py2.py3-none-any.whl

Download URL dotsi-0.0.3-py2.py3-none-any.whl
Size 6.1 kB
Tags Python 2 Python 3
SHA-256 checksum
How to use checksums
fe857974d3c1abe399defef0ea9281caf273da1dcab7c171e1066f1b672aca63
BLAKE2b-256 checksum
How to use checksums
a38dfccad9e5df48d541c4cbf8c19c13db1bc670acf701826b4e6e2619120d4f
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via python-requests/2.24.0

Release history Release notifications | RSS feed

This release

0.0.3 This release

2 release files

0.0.2

2 release files

0.0.1

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page