msiemens / tinydb

TinyDB is a lightweight document oriented database optimized for your happiness :)
https://tinydb.readthedocs.org
MIT License
6.75k stars 536 forks source link

[Bug?] Add option for read-only #136

Closed roniemartinez closed 7 years ago

roniemartinez commented 7 years ago

Hi,

I recently used TinyDB and added it (including the pre-processed JSON data file) to my PyPI package. Everything works fine but not on systems with lower permissions - READ-ONLY.

[Mon May 22 06:42:14.710192 2017] [:error] [pid 5089] [remote 125.60.148.241:45001]   File "/opt/python/run/venv/lib/python3.4/site-packages/tinydb/database.py", line 86, in __init__
[Mon May 22 06:42:14.710196 2017] [:error] [pid 5089] [remote 125.60.148.241:45001]     self._storage = storage(*args, **kwargs)
[Mon May 22 06:42:14.710213 2017] [:error] [pid 5089] [remote 125.60.148.241:45001]   File "/opt/python/run/venv/lib/python3.4/site-packages/tinydb/middlewares.py", line 63, in __call__
[Mon May 22 06:42:14.710217 2017] [:error] [pid 5089] [remote 125.60.148.241:45001]     self.storage = self._storage_cls(*args, **kwargs)
[Mon May 22 06:42:14.710234 2017] [:error] [pid 5089] [remote 125.60.148.241:45001]   File "/opt/python/run/venv/lib/python3.4/site-packages/tinydb/storages.py", line 88, in __init__
[Mon May 22 06:42:14.710248 2017] [:error] [pid 5089] [remote 125.60.148.241:45001]     touch(path, create_dirs=create_dirs)  # Create file if not exists
[Mon May 22 06:42:14.710267 2017] [:error] [pid 5089] [remote 125.60.148.241:45001]   File "/opt/python/run/venv/lib/python3.4/site-packages/tinydb/storages.py", line 23, in touch
[Mon May 22 06:42:14.710270 2017] [:error] [pid 5089] [remote 125.60.148.241:45001]     with open(fname, 'a'):
[Mon May 22 06:42:14.710294 2017] [:error] [pid 5089] [remote 125.60.148.241:45001] PermissionError: [Errno 13] Permission denied: '/opt/python/run/venv/lib/python3.4/site-packages/<filename.json>

As you can see, 'a' mode on open() function is hardcoded. The use case is only to read the file, though.

For now the quick fix is to add write permission to the required JSON file. If there is an option to modify the mode, that will be great!

Thanks.

msiemens commented 7 years ago

Hey! I think the case that a database file is not writable isn't very common. I think the best solution here would be to create a copy of the JSONStorage and adapt it by removing the write functionality (maybe by throwing an exception). More details on custom storages can be found in the docs. What do you think?

roniemartinez commented 7 years ago

Maybe writing a Read-Only JSONStorage is a better idea [for now].

I am also thinking of monkey-patching the touch() function. This is possible, right?

Cheers!

msiemens commented 7 years ago

I am also thinking of monkey-patching the touch() function. This is possible, right?

I think if you take the JSONStorage as it currently is and remove all the stuff that is used to write new entries, you won't need to monkey-patch the touch() function. You could go with something like this:


class JSONStorageReadOnly(Storage):
    """
    Store the data in a JSON file.
    """

    def __init__(self, path):
        """
        Create a new instance.
        :param path: Where to store the JSON data.
        :type path: str
        """

        super(JSONStorage, self).__init__()
        self._handle = open(path, 'r')

    def close(self):
        self._handle.close()

    def read(self):
        return json.load(self._handle)

    def write(self, data):
        raise RuntimeError('JSONStorageReadOnly cannot write data')
roniemartinez commented 7 years ago

Thanks a lot @msiemens :+1:

rafalkrupinski commented 5 years ago

in JSONStorage you open a file and then in the readonly you open it again. That's file description leak. Perhaps a Middleware would be more appropriate?

Edit: or is it actually skipping the JSONStora.init ...?

msiemens commented 5 years ago

@rafalkrupinski You're right actually! Using a middleware would be more correct in this case.

pavelanni commented 3 years ago

If you came to this page looking for the "read-only" option (like I did) -- here you are: there is an access_mode option now. You can open your database like this: db = TinyDB("db.json", access_mode='r') and it will be read-only. Thanks for adding this option, @jmborr !