Skip to main content

Firestore4Kivy

A very basic, lightweight, machine independent Firestore API

2023-11-13 This repository is archived.

Introduction

The package contains methods to authorize and perform CRUD operations on a Firestore database. The data saved and retrieved is a constrained Python dict, constrained by the datatypes and resources available on Firestore. The implementation uses REST APIs.

Data is saved in a 'document' in a 'collection'. Firestore security rules that enable authorized public access to a document, and authorized private access to a document are compatible.

Nothing in this package is Kivy specific, however the motivation for the package is mobile devices and for Python that likely means Kivy.

Example Usage

Assuming a Firebase account has been created, a Firestore project has been configured, and the Firebase all authenticated users rule is specified.

In a real usage each access would be in its own thread.

Obtain the APIKEY and PROJECT_ID of a Firebase project.

from firestore4kivy import Authorize, Firestore
from math import pi

auth = Authorize(APIKEY)
success, response = auth.sign_in_with_email('email', 'password')

if success:
    db = Firestore(PROJECT_ID)
    db.enable_database(response)
    
    data = {'name' : 'Random Hacker', 'favorite number' : pi }
    success, result, update_time = db.create('collection', 'document', data)
    if success:
       success, result, update_time = db.read('collection', 'document')
       if success:
           field_to_update = {'favorite number' : 0 }
           success, result, update_time = db.update('collection', 'document', field_to_update)
           print(result) #{'name' : 'Random Hacker', 'favorite number' : 0 }

See also the example.

Install

Desktop

pip3 install firestore4kivy

Buildozer

requirements = python3, kivy, firestore4kivy, requests, urllib3, charset-normalizer==2.1.1, idna, certifi

android.permissions = INTERNET

kivy-ios

toolchain pip install firestore4kivy

Firestore

Checklist

This checklist is not a substitute for reading the Firebase documentation. See the Firebase quick start, and Firebase documentation.

  • Create a Firebase account and project.

    • This generates the Project ID you will need for client apps.
  • Create API Key

    • Build->Authentication->Get started->sign-in method->Email/Password->Enable->Save
  • Firestore Database->Create Database

    • Select security rules.
    • Edit and publish new security rules.
    • Select a geographic location for the server.
    • Allow a few minutes for changes to propagate.

Security Rules

Successful implementation requires that the security rules are correctly implemented. The default rules deny access and must be changed. For development only, you can use the all authenticated users rule. As the name suggests this requires that the user is signed in.

Private documents will require the content-owner only access rule..

In practice the rules may be some hybrid, see the rules for the example.

Constraints

This section is not a substitute for reading Firestore usage and limits.

Some highlights:

  • The data dict values may contain None or values with Python data types: bool, int, float, str, bytes. The dict may also contain three classes provided to access data types available in Firestore but not in Python, there are GeoPoint, TimeStamp, Reference.

  • The data dict may not contain values with Python data types: bytearray, memoryview, tuple, complex, range, frozenset, set, or any class other than the three that enable Firestore data types.

  • A dict key is always cast to a string. There is potential for a conflict if you use integers and strings as dict keys.

  • A list may not contain a list as an element.

  • A document may have a maximum of slightly less than 20k indices (sum of all keys and all list lengths).

  • A document may have a maximum size of slightly less than 1MB.

If you are uncertain about violating Firestore constraints, as a test you can manually create your data structure in the Firebase console.

API

The methods must be called from a thread. If this is not done the Kivy UI will freeze for an undetermined time. A general example of using threads is documented.

The package contains an Authorize class, a Firestore class, and three classes to access custom Firestore data types.

Authorize

All methods return a tuple of two values, success and response. If success is True, response is a dict. If success is False, response is an error message string.

A user initially signs in and the app saves a refresh token. On restart, resume, or after a timeout the app re-signs in using the token. Firestore will time out 3600 seconds after the last sign in. A sign in response contains the refresh token (save it), and data to enable Firestore (re-enable with each new response).

Authorize Basic Usage

from firestore4kivy import Authorize

# Instantiate with an APIKEY

auth = Authorize(APIKEY)

# sign in

success, response = auth.sign_in_with_email('user', 'password')

Authorize API

    def create_user_with_email(self, email, password):
        return success, response
         
    def sign_in_with_email(self, email, password):
        return success, response

    def sign_in_with_token(self, refresh_token):
        return success, response

    def delete_user(self, response):
        return success, response

Firestore

All methods except enable_database return a tuple of three values, success, response, and update time. If success is True, response is a dict. If success is False, response is an error message string.

Firestore Basic Usage

from firestore4kivy import Firestore

# Instantiate with an PROJECT_ID

db = Firestore(PROJECT_ID)

# Enable the database with the response from sign in
db.enable_database(response)

# Create Document

data = {'name' : 'Joe', 'age' : 99}
success, response, update_time = db.create('collection', 'document', data)

Firestore API

Enable_database

The auth argument is the response from Authorize api calls.

    def enable_database(self, auth):

Create, Read, Delete

The collection and document arguments are strings. The data argument is a dict.

    def create(self, collection, document, data):
        return success, response, update_time

    def read(self, collection, document):
        return success, response, update_time

    def delete(self, collection, document):
        return success, response, update_time

To create a private document the collection or document is set to None, the UserId will be substituted. This enables use of the Firestore the private document rule.

Update

The collection and document arguments are strings. The replace, delete, and callback method arguments are dicts. Examples below.

    def update(self, collection, document, replace = {}, delete ={}, callback=None):
        return success, response, update_time

The update() method specifies changes to a created dict. It has three arguments used to update the document contents: replace, delete, and callback. One or all may be specified, they are applied in the above order.

Update is a secure read-modify-write operation. It ensures that another user will not corrupt the operation, the cost will be increased latency with heavily used shared documents. Single updates take fractions of a second, an update taking several seconds is a sign there are an excessive number of concurrent users and a failed update is possible. Do not implement update() on a shared document if you expect heavy concurrent usage.

In a Python dict, a list is atomic. To access list elements we use a list of tuples [(index, new_value), (index, new_value),.... ] , and specify the semantics as accessing list elements in a dict. Out of range indices are ignored.

Modification dicts are hierarchical. Check that root and intermediate dict keys are only specified once, otherwise one key will overwrite the other.

  • replace: a dict of key value pairs to replace. Pairs that do not exist in the original document are created. An empty dict specifies no modification. Special case: a tuple representing a list index equal to list length implements append(). An example replace dict (taken from the example where you can find the dict to be modified):
                                                     # Equivalent statements
        replace_these = {'b' : False,                # ['b'] = False
                         'd' : {'c' : -43 ,          # ['d']['c'] = -43
                                'e' : [-10,-20,-30]},# ['d']['e'] =[-10,-20,-30]
                         'new' : True,               # ['new'] = True
                         'a' : [(1,'negative'),      # ['a'][1] = 'negative'
                                (4,'new')],}         # ['a'].append('new')
        # Note that for example ['d']['c'] and ['d']['e'] are hierarchical in the dict.
  • delete: a dict of keys to remove, the values should be empty except when specifying hierarchy. Keys that do not exist are ignored. An empty dict removes no keys. A delete of multiple list elements in the same list does not depend on lexical order (unlike the equivalent statements). An example delete dict:
                                                     # Equivalent statements
        delete_these =  {'f':'',                     # pop('f')
                         'd': {'d' : {'aa': ''}},    # ['d']['d'].pop('aa')
                         'a': [(0,''),               # d['a'].pop(0)
                               (3, {'yes': ''})] }   # d['a'][2].pop('yes')
  • callback: a user supplied method that has the dict of modified data as an argument. It can be used to implment a state machine, or for debugging use a print statement to see the modified dict. An example of a state machine:
    def counter(self, data):
        if 'counter' in data:
            data['counter'] += 1
        else:
            data['counter'] = 0

Implement as:

    success, response, update_time =\
        db.update('secrets', None, replace_these, delete_these, self.couner)

GeoPoint, TimeStamp, and Reference

The package provides three classes to access data types that exist in Firestore but not in Python.

All are written with initialize parameters, and read by a get() method.

from firestore4kivy import GeoPoint, Timestamp, Reference

# Latitude and longitude take there usual range

GeoPoint(latitude, longitude).get() == { 'latitude' : latitude, 'longitude' :longitude}

# TimeStamp is a Firestore compatible ISO 8601 string

TimeStamp('2000-01-01T00:00:00Z').get() == '2000-01-01T00:00:00Z'

# Reference to a Firestore document that must exist

Reference('projects/{project}/databases/(default)/documents/{collection}/{document}).get() == 'projects/{project}/databases/(default)/documents/{collection}/{document}

Metadata

Release files for firestore4kivy 0.0.1

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

Source distribution (sdist)

Source distribution for firestore4kivy 0.0.1
File Size Uploaded Provenance Actions
firestore4kivy-0.0.1.tar.gz 14.8 kB Without provenance Details for firestore4kivy-0.0.1.tar.gz

Built distribution (wheel)

Table of built distributions (wheels) for firestore4kivy 0.0.1
File Interpreter ABI Platform Actions
firestore4kivy-0.0.1-py3-none-any.whl Python 3 none any Details for firestore4kivy-0.0.1-py3-none-any.whl

Total release size: 26.1 kB

Release files / firestore4kivy-0.0.1.tar.gz

Download URL firestore4kivy-0.0.1.tar.gz
Size 14.8 kB
Tags Source
SHA-256 checksum
How to use checksums
4cacf9a8c3f1a57fcfbcf402a1f1df961c868d323b9566fe016a366e3c21f587
BLAKE2b-256 checksum
How to use checksums
a1fdb406f2937ab9393bc9a5dca929036b020caefc472d4d44769e3db57a36ff
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/4.0.2 CPython/3.11.1

Release files / firestore4kivy-0.0.1-py3-none-any.whl

Download URL firestore4kivy-0.0.1-py3-none-any.whl
Size 11.3 kB
Tags Python 3
SHA-256 checksum
How to use checksums
f93e6d82d475579fc850a50a3ec635d5118b80f93f74614349250680039cad2d
BLAKE2b-256 checksum
How to use checksums
e09ffd411785f0a5be72f1121e347d2eba7e1efabc9656b4f94f43b140737679
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/4.0.2 CPython/3.11.1

Security for firestore4kivy 0.0.1

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

No provenance found

None of the files in this release were uploaded with provenance metadata.

A note on package security

PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.

To learn more about protecting yourself from malicious actors, visit our package security documentation.

Release history Release notifications | RSS feed

This release

0.0.1 This release

2 release files No provenance

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