Skip to main content

Produtils

This started as "copying the code" from the talk Build powerful, new data structures with Python's abstract base classes by Raymond Hettinger since I couldn't find it anywhere.

After that I started modifying the code and creating new utilities.

Install with:

pip install fsdict

flightcryptofiledict.py

Dictionary implementation that saves everything to files in disk.

Features

When a value is going it be written to the disk a new salt is created. Then this salt is passed to the hashing function. The values are encrypted using PBKDF2HMAC. The salt is generated for each entry. It is saved in the filename separated by a quadruple underscore. Don't use quadruple underscore in your variable names when setting/getting the dictionary, it will break, you can use double and triple underscores. It can be fixed adding a couple of if statements inside, but so far I have never had to create a key/value name with 4*_ inside.

The salt is 64 bytes long and it's created using secrets.token_urlsafe. The salt is created as a string and then encoded to bytes so that it's easier to implement a static salt that you can keep as an environment variable.

Installation

It is a single self-contained 200 lines-of-code file. The only dependency is cryptography. Install it with:

pip install cryptography

I am also using methodtools to have lru_cache for class methods. If you don't want it just remove the import and the decorator.

The best way to use it is just copying and pasting the contents of the file to your project and avoid adding one more dependency. When copying the file you may extend it adding more functionality to it. That functionality may be useful to other users so please consider openning a pull request to add it to the current project.

Usage

You need to provide a password when creating the dictionary object or set an environment variable:

Creating an environment variable (recommended):

export PASS=mypass

When creating the dictionary:

from flightcryptofiledict import FileDict

d = FileDict("newname", password="secretpassword")

Using different data types

If the dictionary values need to be something different from strings a custom encoder/decoder can be passed. The only condition is that those functions should return a bytes object. Examples:

>>> from fsdict.flightcryptofiledict import FileDict
>>> enc_filedict = FileDict("cryptest", password="password")

>>> import pickle
>>> import math

>>> enc_filedict.encoder = lambda x: pickle.dumps(x)
>>> enc_filedict.decoder = lambda x: pickle.loads(x)

>>> enc_filedict["myfunc"] = math.cos
>>> enc_filedict["myfunc"]
<function math.cos(x, /)>

>>> enc_filedict["myfunc"](23)
-0.5328330203333975

>>> import json
>>> enc_filedict.encoder = lambda x: json.dumps(x).encode()
>>> enc_filedict.decoder = lambda x: json.loads(x.decode())

>>> enc_filedict["mydict"] = {"a": 12}
>>> enc_filedict["mydict"]
{'a': 12}

>>> enc_filedict["mydict"]["a"]
12

Compressing to file

Create a tarfile with the same name as the dictionary.

enc_filedict.compress()

You can use a different filename. The **kwargs passed to the function will be passed to the tarfile function.

enc_filedict.compress(filename="compressed", compresslevel=7)

The command above will generate the file: compressed.tar.gz

cryptofiledict.py

More or less the same as before but the salt is static. The salt is parsed as a base64 encoded string. It will be less secure but faster. You can pass the salt as a string when creating a new dictionary. If not, it will try to get it from the environment variables.

sqldict.py

Uses an SQLite database instead of the filesystem.

cryptosqldict.py

Same as flightcryptofiledict.py, but uses an sqlite database instead of the file system. It only needs a password and generates a different salt for each item. The salt is stored in a table column and the data in another one.

Performance

The performance method for each dictionary is calculated like this (adapted to each case):

In [1]: from cryptosqldict import SQLDict as d

In [2]: dd = d("perf_test", password="mypass")

In [3]: from string import ascii_lowercase as letters

In [4]: from random import choice

In [5]: def randstr(n):
   ...:     return "".join([choice(letters) for _ in range(n)])

In [6]: %%timeit
   ...: dd[randstr(10)] = randstr(100)

RESULTS:

cryptosqldict.py: 67.4 ms ± 2.6 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)

sqldict.py: 1.26 ms ± 261 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)

filedict.py: 578 µs ± 40.3 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)

cryptofiledict.py: 809 µs ± 35.2 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)

flightcryptofiledict.py: 68.9 ms ± 1.87 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)

Meta

Ricardo Ander-Egg Aguilar – @ricardoanderegg

Distributed under the MIT license. See LICENSE for more information.

Contributing

  1. Fork it (https://github.com/polyrand/produtils/fork)
  2. Create your feature branch (git checkout -b feature/fooBar)
  3. Commit your changes (git commit -am 'Add some fooBar')
  4. Push to the branch (git push origin feature/fooBar)
  5. Create a new Pull Request

Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

fsdict-0.5.2.tar.gz (10.2 kB view details)

Uploaded Source

Built Distribution

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

fsdict-0.5.2-py3-none-any.whl (11.0 kB view details)

Uploaded Python 3

File details

Details for the file fsdict-0.5.2.tar.gz.

File metadata

  • Download URL: fsdict-0.5.2.tar.gz
  • Upload date:
  • Size: 10.2 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/3.2.0 pkginfo/1.5.0.1 requests/2.24.0 setuptools/47.3.1 requests-toolbelt/0.9.1 tqdm/4.47.0 CPython/3.7.7

File hashes

Hashes for fsdict-0.5.2.tar.gz
Algorithm Hash digest
SHA256 91c27abeae9d4adb045ef4343e8c37bf4de4cdac7f314cc22e1d7f9c81007ea2
MD5 ad321fd813a3503fa03f77ab795c8f4a
BLAKE2b-256 1d66a0896cc9ddf84b41dd10513caed31c6625594ba5660e55316b613862e442

See more details on using hashes here.

File details

Details for the file fsdict-0.5.2-py3-none-any.whl.

File metadata

  • Download URL: fsdict-0.5.2-py3-none-any.whl
  • Upload date:
  • Size: 11.0 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/3.2.0 pkginfo/1.5.0.1 requests/2.24.0 setuptools/47.3.1 requests-toolbelt/0.9.1 tqdm/4.47.0 CPython/3.7.7

File hashes

Hashes for fsdict-0.5.2-py3-none-any.whl
Algorithm Hash digest
SHA256 6a58a1fe98f087075cc77454542a2b9482a353675ab8d8d6999cb466af727176
MD5 5f7da494f415972755ab57d83213ee55
BLAKE2b-256 25abc895e3be698e1f2ada0b3fc3b43cfaaee1b1d6cbec1f6bc7666c2ece282d

See more details on using hashes here.

Release history Release notifications | RSS feed

0.8.1

2 files

0.8

2 files

0.7

2 files

0.6

2 files

0.5.5

2 files

0.5.3

2 files

This release

0.5.2 This release

2 files

0.5.1

2 files

0.5

2 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