├── .coveragerc ├── .gitignore ├── .idea └── vcs.xml ├── .travis.yml ├── LICENSE ├── README.rst ├── ci_scripts ├── build.sh └── setup_packages ├── docs ├── conf.py ├── index.rst ├── install.md ├── micropython_redis.rst └── usage.md ├── documentation_requirements.txt ├── redis.client ├── setup.py └── uredis_modular │ └── client.py ├── redis.connection ├── setup.py └── uredis_modular │ └── connection.py ├── redis.geo ├── setup.py └── uredis_modular │ └── geo.py ├── redis.hash ├── setup.py └── uredis_modular │ └── hash.py ├── redis.key ├── setup.py └── uredis_modular │ └── key.py ├── redis.list ├── setup.py └── uredis_modular │ └── list.py ├── redis.pubsub ├── setup.py └── uredis_modular │ └── pubsub.py ├── redis.set ├── setup.py └── uredis_modular │ └── set.py ├── redis.sortedset ├── setup.py └── uredis_modular │ └── sortedset.py ├── redis.string ├── setup.py └── uredis_modular │ └── string.py ├── tests ├── test_connection.py ├── test_hash.py ├── test_operations_lists.py ├── test_operations_set.py └── test_operations_sortedset.py ├── tests_micropython └── operations_lists.py ├── tox.ini ├── uredis ├── setup.py └── uredis │ ├── __init__.py │ └── uredis.py └── uredis_modular ├── setup.py └── uredis_modular └── __init__.py /.coveragerc: -------------------------------------------------------------------------------- 1 | [run] 2 | branch = True 3 | cover_pylib = False 4 | source= 5 | uredis 6 | 7 | [report] 8 | ignore_errors = True 9 | show_missing = 1 10 | # Regexes for lines to exclude from consideration 11 | exclude_lines = 12 | # Have to re-enable the standard pragma 13 | pragma: no cover 14 | omit= 15 | *psutil/* 16 | *redislite/* 17 | */redis/* 18 | *distutils/* 19 | *ctypes* 20 | *encodings/* 21 | *getopt.py* 22 | *stringprep.py* 23 | *uuid.py* 24 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Byte-compiled / optimized / DLL files 2 | __pycache__/ 3 | *.py[cod] 4 | *$py.class 5 | 6 | # C extensions 7 | *.so 8 | 9 | # Distribution / packaging 10 | .Python 11 | env/ 12 | build/ 13 | develop-eggs/ 14 | dist 15 | downloads/ 16 | eggs/ 17 | .eggs/ 18 | lib/ 19 | lib64/ 20 | parts/ 21 | sdist/ 22 | var/ 23 | *.egg-info/ 24 | .installed.cfg 25 | *.egg 26 | 27 | # PyInstaller 28 | # Usually these files are written by a python script from a template 29 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 30 | *.manifest 31 | *.spec 32 | 33 | # Installer logs 34 | pip-log.txt 35 | pip-delete-this-directory.txt 36 | 37 | # Unit test / coverage reports 38 | htmlcov/ 39 | .tox/ 40 | .coverage 41 | .coverage.* 42 | .cache 43 | nosetests.xml 44 | coverage.xml 45 | *,cover 46 | .hypothesis/ 47 | 48 | # Translations 49 | *.mo 50 | *.pot 51 | 52 | # Django stuff: 53 | *.log 54 | 55 | # Sphinx documentation 56 | docs/_build/ 57 | 58 | # PyBuilder 59 | target/ 60 | 61 | #Ipython Notebook 62 | .ipynb_checkpoints 63 | -------------------------------------------------------------------------------- /.idea/vcs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: python 2 | sudo: false 3 | python: 3.5 4 | env: 5 | - TOX_ENV=py35 6 | notifications: 7 | irc: "chat.freenode.net#micropython-redis" 8 | matrix: 9 | fast_finish: true 10 | allow_failures: 11 | - env: 12 | - TOX_ENV=build_docs 13 | install: 14 | - pip install --upgrade pip setuptools tox virtualenv coveralls 15 | script: 16 | - tox -v -v -e $TOX_ENV 17 | after_success: 18 | coveralls 19 | after_failure: 20 | - for X in .tox/$TOX_ENV/log/*; do echo "$X\n"; cat "$X"; echo "\n\n"; done 21 | - echo "pip.log\n"; cat $HOME/.pip/pip.log 22 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2016 Dwight Hubbard 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.rst: -------------------------------------------------------------------------------- 1 | .. image:: https://travis-ci.org/dwighthubbard/micropython-redis.svg?branch=master 2 | :target: https://travis-ci.org/dwighthubbard/micropython-redis 3 | 4 | .. image:: https://readthedocs.org/projects/micropython-redis/badge/?version=latest 5 | :target: http://micropython-redis.readthedocs.io/en/latest/ 6 | :alt: Documentation 7 | 8 | .. image:: https://img.shields.io/pypi/v/micropython-redis.svg 9 | :target: https://pypi.python.org/pypi/micropython-redis/ 10 | 11 | .. image:: https://img.shields.io/badge/python-micropython-blue.svg 12 | :target: https://pypi.python.org/pypi/micropython-redis/ 13 | 14 | .. image:: https://img.shields.io/pypi/l/micropython-redis.svg 15 | :target: https://pypi.python.org/pypi/micropython-redis/ 16 | 17 | ----------------------------------------------------------------------------------------------------------------------- 18 | 19 | micropython-redis 20 | ================= 21 | 22 | A redis client implementation designed for use with micropython. 23 | 24 | This module is a new redis-client written to be functional when using Micropython on embedded microcontrollers with 25 | limited resources. 26 | 27 | In order to function on microcontrollers without multitasking operating systems the implementation does not use 28 | threading or multiprocessing. As a result functionality that relies on these features such as connection pools 29 | is not available. 30 | 31 | This implementation can utilize ssl and floating point support if it is available but it will operate with reduced 32 | functionality if it is not. 33 | 34 | Current Status 35 | ============== 36 | 37 | Currently this module is not feature complete, here is the current status 38 | 39 | +---------------------+-----------------+-----------+------------------------+ 40 | | Redis Command Group | Implemented | Tests | Notes | 41 | +=====================+=================+===========+========================+ 42 | | Cluster | Not Planned | | | 43 | +---------------------+-----------------+-----------+------------------------+ 44 | | Connection | Complete | 100% | | 45 | +---------------------+-----------------+-----------+------------------------+ 46 | | Geo | Complete | 0% | | 47 | +---------------------+-----------------+-----------+------------------------+ 48 | | Hashes | Complete | 100% | | 49 | +---------------------+-----------------+-----------+------------------------+ 50 | | HyperLogLog | Not Implemented | | | 51 | +---------------------+-----------------+-----------+------------------------+ 52 | | Keys | Completed | 0% | | 53 | +---------------------+-----------------+-----------+------------------------+ 54 | | Lists | Complete | 40% | | 55 | +---------------------+-----------------+-----------+------------------------+ 56 | | Publish/Subscribe | Not Complete | None | API works differently | 57 | | | | | than other | 58 | | | | | functionality, | 59 | | | | | so will likely use more| 60 | | | | | resources and require | 61 | | | | | more work to implement.| 62 | +---------------------+-----------------+-----------+------------------------+ 63 | | Scripting | Not Implemented | | | 64 | +---------------------+-----------------+-----------+------------------------+ 65 | | Server | Not Implemented | | | 66 | +---------------------+-----------------+-----------+------------------------+ 67 | | Sets | Complete | 20% | | 68 | +---------------------+-----------------+-----------+------------------------+ 69 | | Sorted Sets | Complete | 0% | | 70 | +---------------------+-----------------+-----------+------------------------+ 71 | | Strings | Complete | 0% | | 72 | +---------------------+-----------------+-----------+------------------------+ 73 | | Transactions | Not Implemented | | | 74 | +---------------------+-----------------+-----------+------------------------+ 75 | -------------------------------------------------------------------------------- /ci_scripts/build.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | ls -1d uredis redis.*| while read package 4 | do 5 | cd "$package" 6 | python3 setup.py sdist 7 | mv dist/* ../dist 8 | cd .. 9 | done 10 | -------------------------------------------------------------------------------- /ci_scripts/setup_packages: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | import argparse 3 | import contextlib 4 | import os 5 | import subprocess 6 | import sys 7 | 8 | 9 | failed = [] 10 | 11 | 12 | @contextlib.contextmanager 13 | def change_directory(path): 14 | cwd = os.getcwd() 15 | os.chdir(path) 16 | try: 17 | yield 18 | finally: 19 | os.chdir(cwd) 20 | 21 | 22 | def git_revision_count(): 23 | try: 24 | result = subprocess.check_output(['git', 'rev-list', '--all']).decode().split(os.linesep) 25 | except subprocess.CalledProcessError: 26 | result = 0 27 | return len(result) 28 | 29 | 30 | def infer_version(): 31 | version = '0.0.%s' % os.environ.get('TRAVIS_BUILD_NUMBER', str(git_revision_count())) 32 | return version 33 | 34 | def update_dunder_init_version(filename, version): 35 | if not version: 36 | version = infer_version() 37 | if os.path.exists(filename): 38 | print('In filename %r, setting __version__ = %r' % (filename, version)) 39 | infile = None 40 | with open(filename) as read_handle: 41 | infile = read_handle.readlines() 42 | if infile: 43 | with open(filename, 'w') as write_handle: 44 | for line in infile: 45 | if '__version__' in line: 46 | temp = line.split('=') 47 | temp[-1] = ' ' + repr(version) + '\n' 48 | line = '='.join(temp) 49 | write_handle.write(line) 50 | 51 | def update_setup_version(filename=None, version=None): 52 | if not filename: 53 | filename = 'setup.py' 54 | if not version: 55 | version = infer_version() 56 | infile = None 57 | with open(filename) as setup_handle: 58 | infile = setup_handle.readlines() 59 | if infile: 60 | with open(filename, 'w') as setup_handle: 61 | for line in infile: 62 | if 'version=' in line: 63 | temp = line.split('=') 64 | temp[-1] = repr(version) + ',\n' 65 | line = '='.join(temp) 66 | setup_handle.write(line) 67 | 68 | def setup_in_directory(path, setup_arguments=None, version=None): 69 | global failed 70 | 71 | if not version: 72 | version = '0.0.%s' % os.environ.get('TRAVIS_BUILD_NUMBER', '0') 73 | if not setup_arguments: 74 | setup_arguments = ['sdist'] 75 | if not os.path.exists('dist'): 76 | os.mkdir('dist') 77 | print('*'*80) 78 | print('Processing', path) 79 | print('*'*80) 80 | with change_directory(path): 81 | for dunder_init in ['uredis/__init__.py', 'uredis_modular/__init__.py']: 82 | update_dunder_init_version(dunder_init, version) 83 | update_setup_version(version=version) 84 | 85 | try: 86 | subprocess.check_call( 87 | [ 88 | sys.executable, 89 | './setup.py', 90 | ] + setup_arguments 91 | ) 92 | except subprocess.CalledProcessError: 93 | failed.append(path) 94 | for f in os.listdir('dist'): 95 | filename = os.path.join('dist', f) 96 | dest_filename = os.path.join('../dist', f) 97 | if filename.endswith('.tar.gz'): 98 | print('Renaming: %r->%r' % (filename, dest_filename)) 99 | os.rename(filename, dest_filename) 100 | 101 | 102 | def setup_packages(setup_arguments=None): 103 | if not setup_arguments: 104 | setup_arguments = ['sdist'] 105 | for directory in os.listdir('.'): 106 | if directory.startswith('redis.') or directory in ['uredis', 'uredis_modular']: 107 | setup_in_directory(directory, setup_arguments=setup_arguments) 108 | 109 | 110 | if __name__ == '__main__': 111 | parser = argparse.ArgumentParser() 112 | parser.add_argument('setup_argument', nargs='*', default=[]) 113 | args = parser.parse_args() 114 | 115 | os.environ.setdefault('TRAVIS_BUILD_NUMBER', str(git_revision_count())) 116 | setup_packages(setup_arguments=args.setup_argument) 117 | if failed: 118 | print('The following failed: %s' % ','.join(failed)) 119 | -------------------------------------------------------------------------------- /docs/conf.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | import os 3 | from recommonmark.parser import CommonMarkParser 4 | 5 | import uredis as doc_module 6 | 7 | 8 | html_theme = 'default' 9 | version = doc_module.__version__ 10 | release = version 11 | 12 | 13 | if not os.environ.get('READTHEDOCS', None): # only import and set the theme if we're building docs locally 14 | import sphinx_rtd_theme 15 | html_theme = 'sphinx_rtd_theme' 16 | html_theme_path = [sphinx_rtd_theme.get_html_theme_path()] 17 | 18 | # If extensions (or modules to document with autodoc) are in another directory, 19 | # add these directories to sys.path here. If the directory is relative to the 20 | # documentation root, use os.path.abspath to make it absolute, like shown here. 21 | #sys.path.insert(0, os.path.abspath('.')) 22 | 23 | # -- General configuration ------------------------------------------------ 24 | 25 | # If your documentation needs a minimal Sphinx version, state it here. 26 | #needs_sphinx = '1.0' 27 | 28 | # Add any Sphinx extension module names here, as strings. They can be 29 | # extensions coming with Sphinx (named 'sphinx.ext.*') or your custom 30 | # ones. 31 | extensions = [ 32 | 'sphinx.ext.autodoc', 33 | 'sphinx.ext.intersphinx', 34 | 'sphinx.ext.ifconfig', 35 | 'sphinx.ext.viewcode', 36 | 'sphinx.ext.napoleon', 37 | ] 38 | 39 | # Add any paths that contain templates here, relative to this directory. 40 | templates_path = ['_templates'] 41 | 42 | # The suffix of source filenames. 43 | source_suffix = '.rst' 44 | 45 | # The encoding of source files. 46 | #source_encoding = 'utf-8-sig' 47 | 48 | # The master toctree document. 49 | master_doc = 'index' 50 | 51 | # General information about the project. 52 | project = u'uredis' 53 | copyright = u'2016, Dwight Hubbard' 54 | 55 | # The language for content autogenerated by Sphinx. Refer to documentation 56 | # for a list of supported languages. 57 | #language = None 58 | 59 | # There are two options for replacing |today|: either, you set today to some 60 | # non-false value, then it is used: 61 | #today = '' 62 | # Else, today_fmt is used as the format for a strftime call. 63 | #today_fmt = '%B %d, %Y' 64 | 65 | # List of patterns, relative to source directory, that match files and 66 | # directories to ignore when looking for source files. 67 | exclude_patterns = [] 68 | 69 | # The reST default role (used for this markup: `text`) to use for all 70 | # documents. 71 | #default_role = None 72 | 73 | # If true, '()' will be appended to :func: etc. cross-reference text. 74 | #add_function_parentheses = True 75 | 76 | # If true, the current module name will be prepended to all description 77 | # unit titles (such as .. function::). 78 | #add_module_names = True 79 | 80 | # If true, sectionauthor and moduleauthor directives will be shown in the 81 | # output. They are ignored by default. 82 | #show_authors = False 83 | 84 | # The name of the Pygments (syntax highlighting) style to use. 85 | pygments_style = 'sphinx' 86 | 87 | # A list of ignored prefixes for module index sorting. 88 | #modindex_common_prefix = [] 89 | 90 | # If true, keep warnings as "system message" paragraphs in the built documents. 91 | #keep_warnings = False 92 | 93 | 94 | # -- Options for HTML output ---------------------------------------------- 95 | 96 | # The theme to use for HTML and HTML Help pages. See the documentation for 97 | # a list of builtin themes. 98 | # html_theme = 'default' 99 | 100 | # Theme options are theme-specific and customize the look and feel of a theme 101 | # further. For a list of options available for each theme, see the 102 | # documentation. 103 | # html_theme_options = {} 104 | 105 | # Add any paths that contain custom themes here, relative to this directory. 106 | # html_theme_path = [] 107 | 108 | # The name for this set of Sphinx documents. If None, it defaults to 109 | # " v documentation". 110 | # html_title = None 111 | 112 | # A shorter title for the navigation bar. Default is the same as html_title. 113 | # html_short_title = None 114 | 115 | # The name of an image file (relative to this directory) to place at the top 116 | # of the sidebar. 117 | # html_logo = None 118 | 119 | # The name of an image file (within the static path) to use as favicon of the 120 | # docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32 121 | # pixels large. 122 | # html_favicon = None 123 | 124 | # Add any paths that contain custom static files (such as style sheets) here, 125 | # relative to this directory. They are copied after the builtin static files, 126 | # so a file named "default.css" will overwrite the builtin "default.css". 127 | html_static_path = ['_static'] 128 | 129 | # Add any extra paths that contain custom files (such as robots.txt or 130 | # .htaccess) here, relative to this directory. These files are copied 131 | # directly to the root of the documentation. 132 | #html_extra_path = [] 133 | 134 | # If not '', a 'Last updated on:' timestamp is inserted at every page bottom, 135 | # using the given strftime format. 136 | #html_last_updated_fmt = '%b %d, %Y' 137 | 138 | # If true, SmartyPants will be used to convert quotes and dashes to 139 | # typographically correct entities. 140 | #html_use_smartypants = True 141 | 142 | # Custom sidebar templates, maps document names to template names. 143 | #html_sidebars = {} 144 | 145 | # Additional templates that should be rendered to pages, maps page names to 146 | # template names. 147 | #html_additional_pages = {} 148 | 149 | # If false, no module index is generated. 150 | #html_domain_indices = True 151 | 152 | # If false, no index is generated. 153 | #html_use_index = True 154 | 155 | # If true, the index is split into individual pages for each letter. 156 | #html_split_index = False 157 | 158 | # If true, links to the reST sources are added to the pages. 159 | #html_show_sourcelink = True 160 | 161 | # If true, "Created using Sphinx" is shown in the HTML footer. Default is True. 162 | #html_show_sphinx = True 163 | 164 | # If true, "(C) Copyright ..." is shown in the HTML footer. Default is True. 165 | #html_show_copyright = True 166 | 167 | # If true, an OpenSearch description file will be output, and all pages will 168 | # contain a tag referring to it. The value of this option must be the 169 | # base URL from which the finished HTML is served. 170 | #html_use_opensearch = '' 171 | 172 | # This is the file name suffix for HTML files (e.g. ".xhtml"). 173 | #html_file_suffix = None 174 | 175 | # Output file base name for HTML help builder. 176 | htmlhelp_basename = 'redisdoc' 177 | 178 | 179 | # -- Options for LaTeX output --------------------------------------------- 180 | 181 | latex_elements = { 182 | # The paper size ('letterpaper' or 'a4paper'). 183 | #'papersize': 'letterpaper', 184 | 185 | # The font size ('10pt', '11pt' or '12pt'). 186 | #'pointsize': '10pt', 187 | 188 | # Additional stuff for the LaTeX preamble. 189 | #'preamble': '', 190 | } 191 | 192 | # Grouping the document tree into LaTeX files. List of tuples 193 | # (source start file, target name, title, 194 | # author, documentclass [howto, manual, or own class]). 195 | latex_documents = [ 196 | ('index', 'redis.tex', u'WiPy Tools Documentation', 197 | u'Dwight Hubbard', 'manual'), 198 | ] 199 | 200 | # The name of an image file (relative to this directory) to place at the top of 201 | # the title page. 202 | #latex_logo = None 203 | 204 | # For "manual" documents, if this is true, then toplevel headings are parts, 205 | # not chapters. 206 | #latex_use_parts = False 207 | 208 | # If true, show page references after internal links. 209 | #latex_show_pagerefs = False 210 | 211 | # If true, show URL addresses after external links. 212 | #latex_show_urls = False 213 | 214 | # Documents to append as an appendix to all manuals. 215 | #latex_appendices = [] 216 | 217 | # If false, no module index is generated. 218 | #latex_domain_indices = True 219 | 220 | 221 | # -- Options for manual page output --------------------------------------- 222 | 223 | # One entry per manual page. List of tuples 224 | # (source start file, name, description, authors, manual section). 225 | man_pages = [ 226 | ('index', 'wipytools', u'WiPy Tools Documentation', 227 | [u'Dwight Hubbard'], 1) 228 | ] 229 | 230 | # If true, show URL addresses after external links. 231 | #man_show_urls = False 232 | 233 | 234 | # -- Options for Texinfo output ------------------------------------------- 235 | 236 | # Grouping the document tree into Texinfo files. List of tuples 237 | # (source start file, target name, title, author, 238 | # dir menu entry, description, category) 239 | texinfo_documents = [ 240 | ('index', 'redis', u'Redis Client Documentation', 241 | u'Dwight Hubbard', 'redis', 'One line description of project.', 242 | 'Miscellaneous'), 243 | ] 244 | 245 | # Documents to append as an appendix to all manuals. 246 | #texinfo_appendices = [] 247 | 248 | # If false, no module index is generated. 249 | #texinfo_domain_indices = True 250 | 251 | # How to display URL addresses: 'footnote', 'no', or 'inline'. 252 | #texinfo_show_urls = 'footnote' 253 | 254 | # If true, do not generate a @detailmenu in the "Top" node's menu. 255 | #texinfo_no_detailmenu = False 256 | 257 | 258 | # Example configuration for intersphinx: refer to the Python standard library. 259 | intersphinx_mapping = {'http://docs.python.org/': None} 260 | 261 | source_parsers = { 262 | '.md': CommonMarkParser, 263 | } 264 | 265 | source_suffix = ['.rst', '.md'] 266 | -------------------------------------------------------------------------------- /docs/index.rst: -------------------------------------------------------------------------------- 1 | 2 | Micropython Redis - Python Redis Client for embedded environments 3 | ***************************************************************** 4 | micropython-redis provides a client for the Redis Key-Value store in a Python module. 5 | 6 | Table of Contents 7 | ================= 8 | 9 | .. toctree:: 10 | :maxdepth: 2 11 | 12 | install.md 13 | usage.md 14 | micropython_redis.rst 15 | 16 | 17 | Indices and tables 18 | ****************** 19 | 20 | * :ref:`genindex` 21 | * :ref:`modindex` 22 | * :ref:`search` 23 | 24 | -------------------------------------------------------------------------------- /docs/install.md: -------------------------------------------------------------------------------- 1 | # Installation Instructions 2 | 3 | ## Installing on CPython 3 4 | 5 | Although micropython-redis is designed to function with micropython, it is supported on most python 3 interpreters. 6 | Use pip to install on Python3 or PyPy3. 7 | 8 | $ pip install micropython-redis[all] 9 | 10 | ## Installing on micropython 11 | 12 | The installation process differs depending on the version of micropython being used. However the **upip** module 13 | is used to do the installation from the Python package repositories. 14 | 15 | ### Installing on micropython unix 16 | 17 | Use the micropython **upip** module to install on micropython. Different redis functionalities for the redis client 18 | are built into different modules. This allows for the installation of specific redis functionality without taaking 19 | up space for functionality that is not used. The following Will install the **uredis** module with all the component 20 | featues in the default micropython lib directory: 21 | 22 | $ micropython -m upip install micropython-redis 23 | $ micropython -m upip install micropython-redis.connection 24 | $ micropython -m upip install micropython-redis.geo 25 | $ micropython -m upip install micropython-redis.hash 26 | $ micropython -m upip install micropython-redis.key 27 | $ micropython -m upip install micropython-redis.list 28 | $ micropython -m upip install micropython-redis.pubsub 29 | 30 | ### Installing on micropython embedded platforms 31 | 32 | To install on micropython embedded platforms: 33 | 34 | #### Step 1. Create a lib directory to hold the python code for the platform 35 | 36 | If you don't already have a library directory on the local system to hold the micropython packages, create one. 37 | 38 | $ mkdir lib 39 | 40 | #### Step 2. Set the MICROPATH environment variable to the full path of the lib directory. 41 | 42 | Set the MICROPYPATH environment variable to point to the library directory. If you created the directory in the 43 | current directory as shown in **Step 1** you could run: 44 | 45 | $ export MICROPYPATH="`pwd`/lib" 46 | 47 | #### Step 3. Use the upip module to install micropython-redis into the lib directory. 48 | 49 | Use the **upip** module to install the **micropython-redis** package. 50 | 51 | $ micropython -m upip install micropython-redis 52 | 53 | Install the redis packages with the desired redis functionality. 54 | 55 | $ micropython -m upip install micropython-redis.connection 56 | $ micropython -m upip install micropython-redis.geo 57 | $ micropython -m upip install micropython-redis.hash 58 | $ micropython -m upip install micropython-redis.key 59 | $ micropython -m upip install micropython-redis.list 60 | $ micropython -m upip install micropython-redis.pubsub 61 | 62 | #### Step 4. Copy the lib directory to the embedded device. 63 | 64 | Finally copy the lib directory you created to the root of the device filesystem. This varies depending on the method 65 | being used to put files on the device. 66 | -------------------------------------------------------------------------------- /docs/micropython_redis.rst: -------------------------------------------------------------------------------- 1 | Code Documentation 2 | ****************** 3 | 4 | Module 5 | ====== 6 | 7 | .. automodule:: uredis 8 | :synopsis: Redis client 9 | :members: 10 | :inherited-members: 11 | :show-inheritance: 12 | 13 | .. automodule:: uredis_modular.client 14 | :synopsis: Redis client 15 | :members: 16 | :inherited-members: 17 | :show-inheritance: 18 | 19 | .. automodule:: uredis_modular.connection 20 | :synopsis: Redis client with only Redis Connection command functionality 21 | :members: 22 | :inherited-members: 23 | :show-inheritance: 24 | 25 | .. automodule:: uredis_modular.keys 26 | :synopsis: Redis client with Redis Keys command functionality 27 | :members: 28 | :inherited-members: 29 | :show-inheritance: 30 | 31 | .. automodule:: uredis_modular.list 32 | :synopsis: Redis client with Redis Lists command functionality 33 | :members: 34 | :inherited-members: 35 | :show-inheritance: 36 | 37 | uredis.Redis() Class 38 | ==================== 39 | .. autoclass:: uredis.Redis 40 | :members: 41 | :inherited-members: 42 | :show-inheritance: 43 | 44 | uredis.StrictRedis() Class 45 | ========================== 46 | .. autoclass:: uredis.StrictRedis 47 | :members: 48 | :inherited-members: 49 | :show-inheritance: 50 | 51 | uredis_modular.connection.Connection() Class 52 | ============================================= 53 | .. autoclass:: uredis_modular.connection.Connection 54 | :members: 55 | :inherited-members: 56 | :show-inheritance: 57 | 58 | uredis_modular.geo.Geo() Class 59 | =============================== 60 | .. autoclass:: uredis_modular.geo.Geo 61 | :members: 62 | :inherited-members: 63 | :show-inheritance: 64 | 65 | uredis_modular.hash.Hash() Class 66 | ================================= 67 | .. autoclass:: uredis_modular.hash.Hash 68 | :members: 69 | :inherited-members: 70 | :show-inheritance: 71 | 72 | uredis_modular.hyperloglog.HyperLogLog() Class 73 | =============================================== 74 | .. autoclass:: uredis_modular.hyperloglog.HyperLogLog 75 | :members: 76 | :inherited-members: 77 | :show-inheritance: 78 | 79 | uredis_modular.key.Key() Class 80 | =============================== 81 | .. autoclass:: uredis_modular.key.Key 82 | :members: 83 | :inherited-members: 84 | :show-inheritance: 85 | 86 | uredis_modular.list.List() Class 87 | ================================= 88 | .. autoclass:: uredis_modular.list.List 89 | :members: 90 | :inherited-members: 91 | :show-inheritance: 92 | 93 | uredis_modular.pubsub.PubSub() Class 94 | ===================================== 95 | .. autoclass:: uredis_modular.pubsub.PubSub 96 | :members: 97 | :inherited-members: 98 | :show-inheritance: 99 | 100 | uredis_modular.server.Server() Class 101 | ===================================== 102 | .. autoclass:: redis.server.Server 103 | :members: 104 | :inherited-members: 105 | :show-inheritance: 106 | 107 | uredis_modular.set.Set() Class 108 | =============================== 109 | .. autoclass:: uredis_modular.set.Set 110 | :members: 111 | :inherited-members: 112 | :show-inheritance: 113 | 114 | uredis_modular.sortedset.SortedSet() Class 115 | =========================================== 116 | .. autoclass:: uredis_modular.sortedset.SortedSet 117 | :members: 118 | :inherited-members: 119 | :show-inheritance: 120 | 121 | uredis_modular.string.String() Class 122 | ===================================== 123 | .. autoclass:: uredis_modular.string.String 124 | :members: 125 | :inherited-members: 126 | :show-inheritance: 127 | 128 | uredis_modular.transaction.Transaction() Class 129 | =============================================== 130 | .. autoclass:: uredis_modular.transaction.Transaction 131 | :members: 132 | :inherited-members: 133 | :show-inheritance: 134 | -------------------------------------------------------------------------------- /docs/usage.md: -------------------------------------------------------------------------------- 1 | # Usage 2 | 3 | The micropython-redis module provides 3 different redis client interfaces. 4 | Each of which has different benefits and tradeoffs. 5 | 6 | In the following section, the resource estimates are based on usage 7 | using the micropython unix port. The numbers will be different on 8 | other platforms, although the relative amounts used will remain 9 | roughly the same. 10 | 11 | ## The uredis.Redis()/uredis.StrictRedis() classes. 12 | This class is mostly compatible with the redis-py 13 | redis.Redis()/redis.StrictRedis() classes. Which allows a lot of 14 | existing code to work with little or no modifications. 15 | 16 | The tradeoff is this requires the most resources. Currently 17 | importing this module uses currently ~20kb of memory. 18 | 19 | ## The uredis_modular.* classes 20 | The uredis_modular python module contains python modules that 21 | each implement a subset of the redis server functionality. Each 22 | of these modules can be used individully or they can be combined 23 | as mixins to create a Redis class with the desired functionality. 24 | The more functionality used, the more resources the resulting class 25 | will use. 26 | 27 | All of these modules share a common redis.client which currently 28 | uses about ~6.5kb. Then each functionality module increases the 29 | resource usage by 1kb to 6kb depending on the compexity of 30 | the functinality submodule. 31 | 32 | For example using the uredis_modular.list.List() submodule provides 33 | all of the redis server List functionality but uses 10kb to import. 34 | 35 | ## Low level access using the uredis_modular.client.Client() class 36 | The uredis.modular.client.Client() implements the redis protocol 37 | and can be used to communicate to the redis server direcctly without 38 | pulling in any of the funtionality submodules. 39 | This method uses the least resources, requiring ~6.5kb to import. 40 | 41 | This method is not compatible with the redis-py bindings in any way. 42 | Also all communications will need to be encoded/decode to from byte 43 | strings prior to sending. 44 | -------------------------------------------------------------------------------- /documentation_requirements.txt: -------------------------------------------------------------------------------- 1 | recommonmark 2 | micropython-redis>=0.0.10 3 | -------------------------------------------------------------------------------- /redis.client/setup.py: -------------------------------------------------------------------------------- 1 | import sys 2 | # Remove current dir from sys.path, otherwise setuptools will peek up our 3 | # module instead of system. 4 | sys.path.pop(0) 5 | from setuptools import setup 6 | 7 | 8 | setup( 9 | name='micropython-redis.client', 10 | version='0.0.57', 11 | description='redis client module for MicroPython', 12 | long_description="""This is a redis client module implemented specifically for MicroPython. 13 | 14 | As a result, this module does not support functionality not available on embedded environments and it is structured to allow operating in environments with limited resources. 15 | 16 | Note that this module is a work in progress and currently supports just a subset of all of the redis functionality 17 | 18 | Please help with the development if you are interested in this module.""", 19 | url='https://github.com/dhubbard/micropython-redis', 20 | author='Dwight Hubbard', 21 | author_email="dwight@dwighthubbard.com", 22 | install_requires=['micropython-redis-modular'], 23 | maintainer='Dwight Hubbard', 24 | maintainer_email='dwight@dwighthubbard.com', 25 | license='MIT', 26 | packages=['uredis_modular'], 27 | zip_safe=True, 28 | ) 29 | -------------------------------------------------------------------------------- /redis.client/uredis_modular/client.py: -------------------------------------------------------------------------------- 1 | #!/usr/local/bin/micropython 2 | """ 3 | Redis Client for embedded python environments 4 | """ 5 | class RedisError(Exception): 6 | pass 7 | 8 | 9 | class InvalidResponse(RedisError): 10 | pass 11 | 12 | 13 | class ConnectionError(Exception): 14 | pass 15 | 16 | class Unsupported(Exception): 17 | pass 18 | 19 | 20 | class Connection(object): 21 | def __init__(self, host=None, port=6379, use_ssl=False, timeout=10): 22 | """ 23 | Redis Server Connection 24 | 25 | Parameters 26 | ---------- 27 | host : str, optional 28 | Hostname or IP address of the redis server, default='localhost' 29 | 30 | port : int, optional 31 | Port number of the redis server, default=6379 32 | 33 | use_ssl : bool, optional 34 | Use SSL for the connection, default=False 35 | 36 | timeout : int, optional 37 | Socket timeout in seconds, default=10 38 | """ 39 | try: 40 | import usocket as socket 41 | except ImportError: 42 | import socket 43 | 44 | if not host: 45 | host = '192.168.4.2' 46 | 47 | self.host = host 48 | self.port = int(port) 49 | 50 | self.socket = socket.socket() 51 | self.socket.connect(socket.getaddrinfo(self.host, self.port)[0][-1]) 52 | 53 | if use_ssl: 54 | try: # pragma: no cover 55 | import ssl 56 | self.socket = ssl.wrap_socket(self.socket) 57 | except ImportError: # pragma: no cover 58 | raise Unsupported('SSL support is not available') 59 | 60 | def disconnect(self): 61 | self.socket.close() 62 | 63 | def send_command(self, command, *args): 64 | self.socket.send(command.encode()) 65 | for arg in args: 66 | self.socket.send(b' ') 67 | if isinstance(arg, str): 68 | self.socket.send(repr(arg).encode()) 69 | else: 70 | self.socket.send(repr(arg)) 71 | self.socket.send(b'\r\n') 72 | 73 | def readline(self): 74 | line_buffer = b'' 75 | c = self.socket.recv(1) 76 | while c: 77 | line_buffer += c 78 | try: 79 | if len(line_buffer) > 1 and line_buffer[-2:] == b'\r\n': 80 | break 81 | except IndexError: 82 | pass 83 | c = self.socket.recv(1) 84 | return line_buffer 85 | 86 | 87 | class Client(object): 88 | def __init__(self, host=None, port=6379, password=None): 89 | if not host: 90 | host = '192.168.4.2' 91 | self.connection = Connection(host, port) 92 | 93 | if password: 94 | self.connection.send_command('AUTH', password) 95 | 96 | def send_redis_array_string(self, items): 97 | """ 98 | Send a redis array string 99 | 100 | Parameters 101 | ---------- 102 | items : list 103 | The items to be in the redis array string 104 | 105 | Returns 106 | ------- 107 | bytes 108 | Redis RESP bytestream representation of the array 109 | """ 110 | 111 | self.connection.socket.send(b'*') 112 | self.connection.socket.send(str(len(items)).encode()) 113 | self.connection.socket.send(b'\r\n') 114 | 115 | # Add each element to the stream in turn 116 | for item in items: 117 | item_bytestream = self.convert_to_bytestream(item) 118 | 119 | # Add the item length to the stream 120 | self.connection.socket.send(b'$') 121 | self.connection.socket.send(str(len(item_bytestream)).encode()) 122 | self.connection.socket.send(b'\r\n') 123 | self.connection.socket.send(item_bytestream) 124 | self.connection.socket.send(b'\r\n') 125 | 126 | def execute_command(self, command, *args): 127 | self.run_command(command, *args) 128 | return self.get_response() 129 | 130 | def get_response(self): 131 | response = self.connection.readline() 132 | response_type = response[:1].decode() 133 | response_value = response[1:-2] 134 | if response_type == '+': 135 | return response[1:-2] 136 | elif response_type == '-': 137 | raise RedisError(response[1:-2]) 138 | elif response_type == ':': 139 | return int(response[1:-2]) 140 | elif response_type == '$': 141 | length = int(response_value) 142 | if length == -1: 143 | return None 144 | bulk_string = self.connection.socket.recv(length) 145 | self.connection.readline() 146 | return bulk_string 147 | elif response_type == '*': 148 | return [self.get_response() for item in range(int(response_value))] 149 | else: 150 | raise InvalidResponse('Protocol Error: %s' % response.decode()) 151 | 152 | def run_command(self, command, *args): 153 | # self.connection.send_command(command, *args) 154 | args = [command] + list(args) 155 | self.send_redis_array_string(args) 156 | 157 | def convert_to_bytestream(self, value): 158 | """ 159 | Return a bytestream of the value 160 | 161 | Parameters 162 | ---------- 163 | value 164 | 165 | Returns 166 | ------- 167 | bytes 168 | A bytestream of the value 169 | """ 170 | try: 171 | if isinstance(value, float): 172 | return repr(value).encode() 173 | except NameError: 174 | # Platform doesn't support floating point 175 | pass 176 | if isinstance(value, bytes): 177 | return value 178 | elif isinstance(value, int): 179 | return str(value).encode() 180 | elif isinstance(value, str): 181 | return value.encode() 182 | return str(value).encode() 183 | 184 | def save(self): 185 | return self.execute_command('SAVE') 186 | -------------------------------------------------------------------------------- /redis.connection/setup.py: -------------------------------------------------------------------------------- 1 | import sys 2 | # Remove current dir from sys.path, otherwise setuptools will peek up our 3 | # module instead of system. 4 | sys.path.pop(0) 5 | from setuptools import setup 6 | 7 | 8 | setup( 9 | name='micropython-redis.connection', 10 | version='0.0.57', 11 | description='redis connection module for MicroPython', 12 | long_description="""This is a redis connection module implemented specifically for MicroPython. 13 | 14 | As a result, this module does not support functionality not available on embedded environments and it is structured to allow operating in environments with limited resources. 15 | 16 | Note that this module is a work in progress and currently supports just a subset of all of the redis functionality 17 | 18 | Please help with the development if you are interested in this module.""", 19 | url='https://github.com/dhubbard/micropython-redis', 20 | author='Dwight Hubbard', 21 | author_email="dwight@dwighthubbard.com", 22 | install_requires=['micropython-redis.client'], 23 | maintainer='Dwight Hubbard', 24 | maintainer_email='dwight@dwighthubbard.com', 25 | license='MIT', 26 | packages=['uredis_modular'], 27 | zip_safe=True, 28 | ) 29 | -------------------------------------------------------------------------------- /redis.connection/uredis_modular/connection.py: -------------------------------------------------------------------------------- 1 | from .client import Client 2 | 3 | 4 | class Connection(Client): 5 | def auth(self, password): # pragma: no cover 6 | """ 7 | Authenticate to the server 8 | 9 | Parameters 10 | ---------- 11 | password : str 12 | The password to authenticate with 13 | """ 14 | return self.execute_command('AUTH', password) 15 | 16 | def echo(self, *args): 17 | """Echo the given string 18 | 19 | Parameters 20 | ---------- 21 | message : str 22 | The string to echo 23 | """ 24 | return self.execute_command('ECHO', *args) 25 | 26 | def ping(self, *args): 27 | """Ping the server""" 28 | try: 29 | result = self.execute_command('PING', *args) 30 | except: 31 | result = None 32 | 33 | if result == b'PONG': 34 | return True 35 | return False 36 | 37 | def select(self, *args): # pragma: no cover 38 | """ 39 | Change the selected database 40 | 41 | Parameters 42 | ---------- 43 | index : int 44 | The redis database number to switch to 45 | """ 46 | return self.execute_command('SELECT', *args) 47 | 48 | def quit(self, *args): 49 | """Close the connection""" 50 | return self.execute_command('QUIT', *args) -------------------------------------------------------------------------------- /redis.geo/setup.py: -------------------------------------------------------------------------------- 1 | import sys 2 | # Remove current dir from sys.path, otherwise setuptools will peek up our 3 | # module instead of system. 4 | sys.path.pop(0) 5 | from setuptools import setup 6 | 7 | 8 | setup( 9 | name='micropython-redis.geo', 10 | version='0.0.57', 11 | description='redis geo module for MicroPython', 12 | long_description="""This is a redis geo module implemented specifically for MicroPython. 13 | 14 | As a result, this module does not support functionality not available on embedded environments and it is structured to allow operating in environments with limited resources. 15 | 16 | Note that this module is a work in progress and currently supports just a subset of all of the redis functionality 17 | 18 | Please help with the development if you are interested in this module.""", 19 | url='https://github.com/dhubbard/micropython-redis', 20 | author='Dwight Hubbard', 21 | author_email="dwight@dwighthubbard.com", 22 | install_requires=['micropython-redis.client'], 23 | maintainer='Dwight Hubbard', 24 | maintainer_email='dwight@dwighthubbard.com', 25 | license='MIT', 26 | packages=['uredis_modular'], 27 | zip_safe=True, 28 | ) -------------------------------------------------------------------------------- /redis.geo/uredis_modular/geo.py: -------------------------------------------------------------------------------- 1 | from .client import Client 2 | 3 | 4 | class Geo(Client): 5 | def geoadd(self, *args): 6 | """ 7 | Add one or more geospatial items in the geospatial index represented using a sorted set 8 | 9 | Parameters 10 | ---------- 11 | *args 12 | key longitude latitude member [longitude latitude member ...] 13 | 14 | Returns 15 | ------- 16 | int 17 | The number of elements added to the sorted set, not including elements already existing for which the 18 | score was updated. 19 | """ 20 | return self.execute_command('GEOADD', *args) 21 | 22 | def geohash(self, *args): 23 | """ 24 | Members of a geospatial index as geohash strings 25 | 26 | Parameters 27 | ---------- 28 | *args 29 | key member [member ...] 30 | 31 | Returns 32 | ------- 33 | dict 34 | Returns members of a geospatial index as standard geohash strings 35 | """ 36 | return self.execute_command('GEOHASH', *args) 37 | 38 | def geopos(self, *args): 39 | """ 40 | Return longitude and latitude of members of a geospatial index 41 | 42 | Parameters 43 | ---------- 44 | *args 45 | key member [key member ...] 46 | 47 | Returns 48 | ------- 49 | dict 50 | Returns members of a geospatial index as standard geohash strings 51 | """ 52 | return self.execute_command('GEOPOS', *args) 53 | 54 | def geodist(self, *args): 55 | """ 56 | Returns the distance between two members of a geospatial index 57 | 58 | Parameters 59 | ---------- 60 | *args 61 | key member1 member2 [unit] 62 | Returns 63 | ------- 64 | """ 65 | return self.execute_command('GEODIST', *args) 66 | 67 | def georadius(self, *args): 68 | """ 69 | Query a sorted set representing a geospatial index to fetch members matching a given maximum distance from a point 70 | 71 | Parameters 72 | ---------- 73 | *args 74 | key longitude latitude radius m|km|ft|mi [WITHCOORD] [WITHDIST] [WITHHASH] [COUNT count] [ASC|DESC] [STORE key] [STOREDIST key] 75 | 76 | Returns 77 | ------- 78 | """ 79 | return self.execute_command('GEORADIUS', *args) 80 | 81 | def georadiusbymember(self, *args): 82 | """ 83 | Query a sorted set representing a geospatial index to fetch members matching a given maximum distance from a member 84 | 85 | Parameters 86 | ---------- 87 | *args 88 | key member radius m|km|ft|mi [WITHCOORD] [WITHDIST] [WITHHASH] [COUNT count] [ASC|DESC] [STORE key] [STOREDIST key] 89 | 90 | Returns 91 | ------- 92 | """ 93 | return self.execute_command('GEORADIUSBYMEMBER', *args) 94 | -------------------------------------------------------------------------------- /redis.hash/setup.py: -------------------------------------------------------------------------------- 1 | import sys 2 | # Remove current dir from sys.path, otherwise setuptools will peek up our 3 | # module instead of system. 4 | sys.path.pop(0) 5 | from setuptools import setup 6 | 7 | 8 | setup( 9 | name='micropython-redis.hash', 10 | version='0.0.57', 11 | description='redis hash module for MicroPython', 12 | long_description="""This is a redis hash module implemented specifically for MicroPython. 13 | 14 | As a result, this module does not support functionality not available on embedded environments and it is structured to allow operating in environments with limited resources. 15 | 16 | Note that this module is a work in progress and currently supports just a subset of all of the redis functionality 17 | 18 | Please help with the development if you are interested in this module.""", 19 | url='https://github.com/dhubbard/micropython-redis', 20 | author='Dwight Hubbard', 21 | author_email="dwight@dwighthubbard.com", 22 | install_requires=['micropython-redis.client'], 23 | maintainer='Dwight Hubbard', 24 | maintainer_email='dwight@dwighthubbard.com', 25 | license='MIT', 26 | packages=['uredis_modular'], 27 | zip_safe=True, 28 | ) -------------------------------------------------------------------------------- /redis.hash/uredis_modular/hash.py: -------------------------------------------------------------------------------- 1 | from .client import Client 2 | 3 | 4 | class Hash(Client): 5 | def hdel(self, *args): 6 | return self.execute_command('HDEL', *args) 7 | 8 | def hexists(self, *args): 9 | return self.execute_command('HEXISTS', *args) 10 | 11 | def hget(self, *args): 12 | return self.execute_command('HGET', *args) 13 | 14 | def hgetall(self, *args): 15 | """" 16 | Returns all fields and values of the hash stored at key. 17 | 18 | Returns 19 | ------- 20 | dict 21 | Dictionary of all key/values from the field 22 | """ 23 | result_dict = {} 24 | result = self.execute_command('HGETALL', *args) 25 | for value in range(0, len(result), 2): 26 | result_dict[result[value]] = result[value+1] 27 | return result_dict 28 | 29 | def hincrby(self, key, field, increment): 30 | """ 31 | Increments the number stored at field in the hash stored at key by increment. If key does not exist, a new 32 | key holding a hash is created. If field does not exist the value is set to 0 before the operation is performed. 33 | 34 | The range of values supported by HINCRBY is limited to 64 bit signed integers. 35 | 36 | Parameters 37 | ---------- 38 | key : str 39 | Hash key to increment 40 | 41 | field : str 42 | Hash field to increment 43 | 44 | increment : int 45 | Amount to increment 46 | 47 | Returns 48 | ------- 49 | int 50 | The value at field after the increment operation. 51 | """ 52 | return self.execute_command('HINCRBY', key, field, int(increment)) 53 | 54 | def hincrbyfloat(self, *args): 55 | return float(self.execute_command('HINCRBYFLOAT', *args)) 56 | 57 | def hkeys(self, *args): 58 | return self.execute_command('HKEYS', *args) 59 | 60 | def hlen(self, *args): 61 | return self.execute_command('HLEN', *args) 62 | 63 | def hmget(self, *args): 64 | return self.execute_command('HMGET', *args) 65 | 66 | def hset(self, *args): 67 | return self.execute_command('HSET', *args) 68 | 69 | def hsetnx(self, *args): 70 | return self.execute_command('HSETNX', *args) 71 | 72 | def hstrlen(self, *args): 73 | return self.execute_command('HSTRLEN', *args) 74 | 75 | def hvals(self, *args): 76 | return self.execute_command('HVALS', *args) 77 | 78 | def hscan(self, *args): 79 | return self.execute_command('HSCAN', *args) 80 | -------------------------------------------------------------------------------- /redis.key/setup.py: -------------------------------------------------------------------------------- 1 | import sys 2 | # Remove current dir from sys.path, otherwise setuptools will peek up our 3 | # module instead of system. 4 | sys.path.pop(0) 5 | from setuptools import setup 6 | 7 | 8 | setup( 9 | name='micropython-redis.key', 10 | version='0.0.57', 11 | description='redis key module for MicroPython', 12 | long_description="""This is a redis key module implemented specifically for MicroPython. 13 | 14 | As a result, this module does not support functionality not available on embedded environments and it is structured to allow operating in environments with limited resources. 15 | 16 | Note that this module is a work in progress and currently supports just a subset of all of the redis functionality 17 | 18 | Please help with the development if you are interested in this module.""", 19 | url='https://github.com/dhubbard/micropython-redis', 20 | author='Dwight Hubbard', 21 | author_email="dwight@dwighthubbard.com", 22 | install_requires=['micropython-redis.client'], 23 | maintainer='Dwight Hubbard', 24 | maintainer_email='dwight@dwighthubbard.com', 25 | license='MIT', 26 | packages=['uredis_modular'], 27 | zip_safe=True, 28 | ) -------------------------------------------------------------------------------- /redis.key/uredis_modular/key.py: -------------------------------------------------------------------------------- 1 | from .client import Client 2 | 3 | 4 | class Key(Client): 5 | def delete(self, *args): 6 | return self.execute_command('DEL', *args) 7 | 8 | def dump(self, *args): 9 | return self.execute_command('DUMP', *args) 10 | 11 | def exists(self, *args): 12 | return self.execute_command('EXISTS', *args) 13 | 14 | def expire(self, *args): 15 | return self.execute_command('EXPIRE', *args) 16 | 17 | def expireat(self, *args): 18 | return self.execute_command('EXPIREAT', *args) 19 | 20 | def get(self, *args): 21 | return self.execute_command('GET', *args) 22 | 23 | def keys(self, *args): 24 | if not args: 25 | args = ['*'] 26 | return self.execute_command('KEYS', *args) 27 | 28 | def migrate(self, *args): 29 | return self.execute_command('MIGRATE', *args) 30 | 31 | def move(self, *args): 32 | return self.execute_command('MOVE', *args) 33 | 34 | def object(self, *args): 35 | return self.execute_command('OBJECT', *args) 36 | 37 | def persist(self, *args): 38 | return self.execute_command('PERSIST', *args) 39 | 40 | def pexpire(self, *args): 41 | return self.execute_command('PEXPIRE', *args) 42 | 43 | def pttl(self, *args): 44 | return self.execute_command('PTTL', *args) 45 | 46 | def randomkey(self, *args): 47 | return self.execute_command('RANDOMKEY', *args) 48 | 49 | def rename(self, *args): 50 | return self.execute_command('RENAME', *args) 51 | 52 | def renamenx(self, *args): 53 | return self.execute_command('RENAMENX', *args) 54 | 55 | def restore(self, *args): 56 | return self.execute_command('RESTORE', *args) 57 | 58 | def scan(self, *args): 59 | return self.execute_command('SCAN', *args) 60 | 61 | def set(self, *args): 62 | if self.execute_command('SET', *args) in [b'OK']: 63 | return True 64 | return False 65 | 66 | def sort(self, *args): 67 | return self.execute_command('SORT', *args) 68 | 69 | def ttl(self, *args): 70 | return self.execute_command('TTL', *args) 71 | 72 | def type(self, *args): 73 | return self.execute_command('TYPE', *args) 74 | 75 | def wait(self, *args): 76 | return self.execute_command('WAIT', *args) 77 | -------------------------------------------------------------------------------- /redis.list/setup.py: -------------------------------------------------------------------------------- 1 | import sys 2 | # Remove current dir from sys.path, otherwise setuptools will peek up our 3 | # module instead of system. 4 | sys.path.pop(0) 5 | from setuptools import setup 6 | 7 | 8 | setup( 9 | name='micropython-redis.list', 10 | version='0.0.57', 11 | description='redis client module for MicroPython', 12 | long_description="""This is a redis client module implemented specifically for MicroPython. 13 | 14 | As a result, this module does not support functionality not available on embedded environments and it is structured to allow operating in environments with limited resources. 15 | 16 | Note that this module is a work in progress and currently supports just a subset of all of the redis functionality 17 | 18 | Please help with the development if you are interested in this module.""", 19 | url='https://github.com/dhubbard/micropython-redis', 20 | author='Dwight Hubbard', 21 | author_email="dwight@dwighthubbard.com", 22 | install_requires=['micropython-redis.client'], 23 | maintainer='Dwight Hubbard', 24 | maintainer_email='dwight@dwighthubbard.com', 25 | license='MIT', 26 | packages=['uredis_modular'], 27 | zip_safe=True, 28 | ) -------------------------------------------------------------------------------- /redis.list/uredis_modular/list.py: -------------------------------------------------------------------------------- 1 | from .client import Client 2 | 3 | 4 | class List(Client): 5 | """ 6 | Redis Client with support for all Redis List operations 7 | """ 8 | def blpop(self, *keys, **kwargs): 9 | """ 10 | Remove and get the first element of a list or block until one is available 11 | 12 | Parameters 13 | ---------- 14 | *keys 15 | Key or keys to get the first element from 16 | 17 | timeout : int, optional 18 | Maximum time to block waiting for the key, if not specified will wait forever. 19 | 20 | Returns 21 | ------- 22 | First element from the list, or None 23 | """ 24 | timeout = kwargs.get('timeout', 0) 25 | keys = tuple(list(keys) + [timeout]) 26 | result = self.execute_command('BLPOP', *keys) 27 | if result == []: 28 | return None 29 | return result 30 | 31 | def brpop(self, *keys, **kwargs): 32 | """ 33 | Remove and get the last element of a list or block until one is available 34 | 35 | Parameters 36 | ---------- 37 | *keys 38 | Key or keys to get the first element from 39 | 40 | timeout : int, optional 41 | Maximum time to block waiting for the key, if not specified will wait forever. 42 | 43 | Returns 44 | ------- 45 | First element from the list, or None 46 | """ 47 | timeout = kwargs.get('timeout', 0) 48 | keys = tuple(list(keys) + [timeout]) 49 | result = self.execute_command('BRPOP', *keys) 50 | if result == [] or result == (): 51 | return None 52 | return tuple(result) 53 | 54 | def brpoplpush(self, src, dst, timeout=0): 55 | """ 56 | Remove and get the last element of a list and push it to the front of another list, blocking if there is no 57 | value to available. 58 | 59 | Parameters 60 | ---------- 61 | src : str 62 | Key to pop the value from 63 | 64 | dst : str 65 | Key to prepend the value to 66 | 67 | timeout : int, optional 68 | Maximum time to block waiting for a key, if not specified or the value is 0, will wait forever. 69 | 70 | Returns 71 | ------- 72 | bytes 73 | The bytestring of the value retrievied from the src 74 | """ 75 | result = self.execute_command('BRPOPLPUSH', src, dst, timeout) 76 | if result == []: 77 | return None 78 | return result 79 | 80 | def lindex(self, *args): 81 | return self.execute_command('LINDEX', *args) 82 | 83 | def linsert(self, *args): 84 | return self.execute_command('LINSERT', *args) 85 | 86 | def llen(self, *args): 87 | return self.execute_command('LLEN', *args) 88 | 89 | def lpop(self, *args): 90 | return self.execute_command('LPOP', *args) 91 | 92 | def lpush(self, *args): 93 | return self.execute_command('LPUSH', *args) 94 | 95 | def lpushx(self, *args): 96 | return self.execute_command('LPUSHX', *args) 97 | 98 | def lrange(self, *args): 99 | return self.execute_command('LRANGE', *args) 100 | 101 | def lrem(self, *args): 102 | return self.execute_command('LREM', *args) 103 | 104 | def lset(self, *args): 105 | return self.execute_command('LSET', *args) 106 | 107 | def ltrim(self, *args): 108 | return self.execute_command('LTRIM', *args) 109 | 110 | def rpop(self, name): 111 | """ 112 | Remove and get the last element of a list 113 | 114 | Parameters 115 | ---------- 116 | name : str 117 | Key to pop the value from 118 | 119 | Returns 120 | ------- 121 | bytes 122 | The bytestring of the value retrievied from the src 123 | """ 124 | return self.execute_command('RPOP', name) 125 | 126 | def rpoplpush(self, *args): 127 | return self.execute_command('RPOPLPUSH', *args) 128 | 129 | def rpush(self, *args): 130 | return self.execute_command('RPUSH', *args) 131 | 132 | def rpushx(self, *args): 133 | return self.execute_command('RPUSHX', *args) 134 | -------------------------------------------------------------------------------- /redis.pubsub/setup.py: -------------------------------------------------------------------------------- 1 | import sys 2 | # Remove current dir from sys.path, otherwise setuptools will peek up our 3 | # module instead of system. 4 | sys.path.pop(0) 5 | from setuptools import setup 6 | 7 | 8 | setup( 9 | name='micropython-redis.pubsub', 10 | version='0.0.57', 11 | description='redis pubsub module for MicroPython', 12 | long_description="""This is a redis pubsub module implemented specifically for MicroPython. 13 | 14 | As a result, this module does not support functionality not available on embedded environments and it is structured to allow operating in environments with limited resources. 15 | 16 | Note that this module is a work in progress and currently supports just a subset of all of the redis functionality 17 | 18 | Please help with the development if you are interested in this module.""", 19 | url='https://github.com/dhubbard/micropython-redis', 20 | author='Dwight Hubbard', 21 | author_email="dwight@dwighthubbard.com", 22 | install_requires=['micropython-redis.client'], 23 | maintainer='Dwight Hubbard', 24 | maintainer_email='dwight@dwighthubbard.com', 25 | license='MIT', 26 | packages=['uredis_modular'], 27 | zip_safe=True, 28 | ) -------------------------------------------------------------------------------- /redis.pubsub/uredis_modular/pubsub.py: -------------------------------------------------------------------------------- 1 | from .client import Client 2 | 3 | 4 | class PubSub(Client): 5 | def psubscribe(self, *args): 6 | return self.execute_command('PSUBSCRIBE', *args) 7 | 8 | def pubsub(self, *args): 9 | return self.execute_command('PUBSUB', *args) 10 | 11 | def publish(self, *args): 12 | return self.execute_command('PUBLISH', *args) 13 | 14 | def punsubscribe(self, *args): 15 | return self.execute_command('PUNSUBSCRIBE', *args) 16 | 17 | def psubscribe(self, *args): 18 | return self.execute_command('PSUBSCRIBE', *args) 19 | 20 | def subscribe(self, *args): 21 | for channel in args[1:]: 22 | self.run_command('SUBSCRIBE', channel) 23 | while True: 24 | self.get_response() 25 | 26 | def unsubscribe(self, *args): 27 | return self.execute_command('UNSUBSCRIBE', *args) 28 | -------------------------------------------------------------------------------- /redis.set/setup.py: -------------------------------------------------------------------------------- 1 | import sys 2 | # Remove current dir from sys.path, otherwise setuptools will peek up our 3 | # module instead of system. 4 | sys.path.pop(0) 5 | from setuptools import setup 6 | 7 | 8 | setup( 9 | name='micropython-redis.set', 10 | description='redis set module for MicroPython', 11 | long_description="""This is a redis set module implemented specifically for MicroPython. 12 | 13 | As a result, this module does not support functionality not available on embedded environments and it is structured to allow operating in environments with limited resources. 14 | 15 | Note that this module is a work in progress and currently supports just a subset of all of the redis functionality 16 | 17 | Please help with the development if you are interested in this module.""", 18 | url='https://github.com/dhubbard/micropython-redis', 19 | author='Dwight Hubbard', 20 | author_email="dwight@dwighthubbard.com", 21 | install_requires=['micropython-redis.client'], 22 | license='MIT', 23 | maintainer='Dwight Hubbard', 24 | maintainer_email='dwight@dwighthubbard.com', 25 | packages=['uredis_modular'], 26 | version='0.0.57', 27 | zip_safe=True, 28 | ) -------------------------------------------------------------------------------- /redis.set/uredis_modular/set.py: -------------------------------------------------------------------------------- 1 | from .client import Client 2 | 3 | 4 | class Set(Client): 5 | def sadd(self, *args): 6 | return self.execute_command('SADD', *args) 7 | 8 | def scard(self, *args): 9 | return self.execute_command('SCARD', *args) 10 | 11 | def sdiff(self, *args): 12 | return set(self.execute_command('SDIFF', *args)) 13 | 14 | def sdiffstore(self, *args): 15 | return self.execute_command('SDIFFSTORE', *args) 16 | 17 | def sinter(self, *args): 18 | return self.execute_command('SINTER', *args) 19 | 20 | def sinterstore(self, *args): 21 | return self.execute_command('SINTERSTORE', *args) 22 | 23 | def sismember(self, *args): 24 | return self.execute_command('SISMEMBER', *args) 25 | 26 | def smembers(self, *args): 27 | return self.execute_command('SMEMBERS', *args) 28 | 29 | def smove(self, *args): 30 | return self.execute_command('SMOVE', *args) 31 | 32 | def spop(self, *args): 33 | return self.execute_command('SPOP', *args) 34 | 35 | def srandmember(self, *args): 36 | return self.execute_command('SRANDMEMBER', *args) 37 | 38 | def srem(self, *args): 39 | return self.execute_command('SREM', *args) 40 | 41 | def sunion(self, *args): 42 | return self.execute_command('SUNION', *args) 43 | 44 | def sunionstore(self, *args): 45 | return self.execute_command('SUNIONSTORE', *args) 46 | 47 | def sscan(self, *args): 48 | return self.execute_command('SCAN', *args) 49 | -------------------------------------------------------------------------------- /redis.sortedset/setup.py: -------------------------------------------------------------------------------- 1 | import sys 2 | # Remove current dir from sys.path, otherwise setuptools will peek up our 3 | # module instead of system. 4 | sys.path.pop(0) 5 | from setuptools import setup 6 | 7 | 8 | setup( 9 | name='micropython-redis.sortedset', 10 | description='redis set module for MicroPython', 11 | long_description="""This is a redis set module implemented specifically for MicroPython. 12 | 13 | As a result, this module does not support functionality not available on embedded environments and it is structured to allow operating in environments with limited resources. 14 | 15 | Note that this module is a work in progress and currently supports just a subset of all of the redis functionality 16 | 17 | Please help with the development if you are interested in this module.""", 18 | url='https://github.com/dhubbard/micropython-redis', 19 | author='Dwight Hubbard', 20 | author_email="dwight@dwighthubbard.com", 21 | install_requires=['micropython-redis.client'], 22 | license='MIT', 23 | maintainer='Dwight Hubbard', 24 | maintainer_email='dwight@dwighthubbard.com', 25 | packages=['uredis_modular'], 26 | version='0.0.57', 27 | zip_safe=True, 28 | ) -------------------------------------------------------------------------------- /redis.sortedset/uredis_modular/sortedset.py: -------------------------------------------------------------------------------- 1 | from .client import Client 2 | 3 | 4 | class SortedSet(Client): 5 | 6 | def zadd(self, name, *args, **kwargs): 7 | """ 8 | Set any number of score, element-name pairs to the key name. Pairs can be specified in two ways: 9 | 10 | As *args, in the form of: score1, name1, score2, name2 11 | 12 | Parameters 13 | ---------- 14 | name : str 15 | Keyname of the list 16 | 17 | *args : 18 | Sequency of name,score values 19 | 20 | """ 21 | new_args = [name] 22 | for index in range(0, len(args), 2): 23 | name = args[index] 24 | value = args[index+1] 25 | if isinstance(value, int): 26 | value = float(value) 27 | new_args.append(value) 28 | new_args.append(name) 29 | return self.execute_command('ZADD', *new_args) 30 | 31 | def zcard(self, *args): 32 | return self.execute_command('ZCARD', *args) 33 | 34 | def zcount(self, *args): 35 | return self.execute_command('ZCOUNT', *args) 36 | 37 | def zincrby(self, *args): 38 | return self.execute_command('ZINCRBY', *args) 39 | 40 | def zinterstore(self, *args): 41 | return self.execute_command('ZINTERSTORE', *args) 42 | 43 | def zlexcount(self, *args): 44 | return self.execute_command('ZLEXCOUNT', *args) 45 | 46 | def zrange(self, *args): 47 | return self.execute_command('ZRANGE', *args) 48 | 49 | def zrangebylex(self, *args): 50 | return self.execute_command('ZRANGEBYLEX', *args) 51 | 52 | def zrevrangebylex(self, *args): 53 | return self.execute_command('ZREVRANGEBYLEX', *args) 54 | 55 | def zrangebyscore(self, *args): 56 | return self.execute_command('ZRANGEBYSCORE', *args) 57 | 58 | def zrank(self, *args): 59 | return self.execute_command('ZRANK', *args) 60 | 61 | def zrem(self, *args): 62 | return self.execute_command('ZREM', *args) 63 | 64 | def zremrangebylex(self, *args): 65 | return self.execute_command('ZREMRANGEBYLEX', *args) 66 | 67 | def zremrangebyrank(self, *args): 68 | return self.execute_command('ZREMRANGEBYRANK', *args) 69 | 70 | def zremrangebyscore(self, *args): 71 | return self.execute_command('ZREMRANGEBYSCORE', *args) 72 | 73 | def zrevrange(self, *args): 74 | return self.execute_command('ZREVRANGE', *args) 75 | 76 | def zrevrangebyscore(self, *args): 77 | return self.execute_command('ZREVRANGEBYSCORE', *args) 78 | 79 | def zrevrank(self, *args): 80 | return self.execute_command('ZREVRANK', *args) 81 | 82 | def zscore(self, *args): 83 | return self.execute_command('ZSCORE', *args) 84 | 85 | def zunionstore(self, *args): 86 | return self.execute_command('ZUNIONSTORE', *args) 87 | 88 | def zscan(self, *args): 89 | return self.execute_command('ZSCAN', *args) 90 | -------------------------------------------------------------------------------- /redis.string/setup.py: -------------------------------------------------------------------------------- 1 | import sys 2 | # Remove current dir from sys.path, otherwise setuptools will peek up our 3 | # module instead of system. 4 | sys.path.pop(0) 5 | from setuptools import setup 6 | 7 | 8 | setup( 9 | name='micropython-redis.string', 10 | description='redis set module for MicroPython', 11 | long_description="""This is a redis string module implemented specifically for MicroPython. 12 | 13 | As a result, this module does not support functionality not available on embedded environments and it is structured to allow operating in environments with limited resources. 14 | 15 | Note that this module is a work in progress and currently supports just a subset of all of the redis functionality 16 | 17 | Please help with the development if you are interested in this module.""", 18 | url='https://github.com/dhubbard/micropython-redis', 19 | author='Dwight Hubbard', 20 | author_email="dwight@dwighthubbard.com", 21 | install_requires=['micropython-redis.client'], 22 | license='MIT', 23 | maintainer='Dwight Hubbard', 24 | maintainer_email='dwight@dwighthubbard.com', 25 | packages=['uredis_modular'], 26 | version='0.0.57', 27 | zip_safe=True, 28 | ) -------------------------------------------------------------------------------- /redis.string/uredis_modular/string.py: -------------------------------------------------------------------------------- 1 | from .client import Client 2 | 3 | 4 | class String(Client): 5 | 6 | def append(self, *args): 7 | return self.execute_command('APPEND', *args) 8 | 9 | def bitcount(self, *args): 10 | return self.execute_command('BITCOUNT', *args) 11 | 12 | def bitfield(self, *args): 13 | return self.execute_command('BITFIELD', *args) 14 | 15 | def bitop(self, *args): 16 | return self.execute_command('BITOP', *args) 17 | 18 | def bitpos(self, *args): 19 | return self.execute_command('BITPOS', *args) 20 | 21 | def decr(self, *args): 22 | return self.execute_command('DECR', *args) 23 | 24 | def DECRBY(self, *args): 25 | return self.execute_command('DECRBY', *args) 26 | 27 | def get(self, *args): 28 | return self.execute_command('GET', *args) 29 | 30 | def getbit(self, *args): 31 | return self.execute_command('GETBET', *args) 32 | 33 | def getrange(self, *args): 34 | return self.execute_command('GETRANGE', *args) 35 | 36 | def getset(self, *args): 37 | return self.execute_command('GETSET', *args) 38 | 39 | def incr(self, *args): 40 | return self.execute_command('INCR', *args) 41 | 42 | def incrby(self, *args): 43 | return self.execute_command('INCRBY', *args) 44 | 45 | def incrbyfloat(self, *args): 46 | return self.execute_command('incrbyfloat', *args) 47 | 48 | def mget(self, *args): 49 | return self.execute_command('MGET', *args) 50 | 51 | def mset(self, *args): 52 | return self.execute_command('MSET', *args) 53 | 54 | def msetnx(self, *args): 55 | return self.execute_command('MSETNX', *args) 56 | 57 | def psetex(self, *args): 58 | return self.execute_command('PSETEX', *args) 59 | 60 | def set(self, *args): 61 | return self.execute_command('SET', *args) 62 | 63 | def setbit(self, *args): 64 | return self.execute_command('SETBIT', *args) 65 | 66 | def setex(self, *args): 67 | return self.execute_command('SETEX', *args) 68 | 69 | def setnx(self, *args): 70 | return self.execute_command('SETNX', *args) 71 | 72 | def setrange(self, *args): 73 | return self.execute_command('SETRANGE', *args) 74 | 75 | def strlen(self, *args): 76 | return self.execute_command('STRLEN', *args) 77 | -------------------------------------------------------------------------------- /tests/test_connection.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | """ 3 | Tests to validate redis list functionality is working properly with micropython 4 | """ 5 | # Notes: 6 | # 7 | # 1. These tests should be run with a cpython interpreter with the redislite module installed. 8 | # 2. The micropython executable should be accesible in the path. 9 | import logging 10 | import redis 11 | import redislite 12 | from unittest import main, TestCase 13 | import uredis 14 | 15 | 16 | class TestRedisConnection(TestCase): 17 | redis_test_port = 7900 18 | 19 | def setUp(self): 20 | self.redis_server = redislite.Redis(serverconfig={'port': self.redis_test_port}) 21 | self.uredis_client = uredis.Redis(host='127.0.0.1', port=self.redis_test_port) 22 | 23 | def tearDown(self): 24 | if self.redis_server: 25 | self.redis_server.shutdown() 26 | 27 | def test_auth(self): 28 | redis_server = redislite.Redis( 29 | serverconfig={ 30 | 'requirepass': 'test', 31 | 'port': self.redis_test_port+1 32 | }, 33 | password='test' 34 | ) 35 | 36 | 37 | # This shouldn't generate an exception 38 | try: 39 | redis_client = redis.Redis(host='127.0.0.1', port=self.redis_test_port+1, password='test') 40 | uredis_client = uredis.Redis(host='127.0.0.1', port=self.redis_test_port+1, password='test') 41 | finally: 42 | redis_server.shutdown() 43 | 44 | def test_echo(self): 45 | result = self.redis_server.echo("test") 46 | uresult = self.uredis_client.echo("test") 47 | self.assertEqual(uresult, result) 48 | 49 | def test_ping(self): 50 | result = self.redis_server.ping() 51 | uresult = self.uredis_client.ping() 52 | self.assertEqual(uresult, result) 53 | 54 | def test_quit(self): 55 | uresult = self.uredis_client.quit() 56 | self.uredis_client.ping() # This should fail since the server is now shutdown 57 | 58 | 59 | if __name__ == '__main__': 60 | logger = logging.getLogger('redislite.client') 61 | logger.setLevel(logging.INFO) 62 | logger.propagate = False 63 | main() 64 | -------------------------------------------------------------------------------- /tests/test_hash.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | """ 3 | Tests to validate redis list functionality is working properly with micropython 4 | """ 5 | # Notes: 6 | # 7 | # 1. These tests should be run with a cpython interpreter with the redislite module installed. 8 | # 2. The micropython executable should be accesible in the path. 9 | import logging 10 | import redis 11 | import redislite 12 | from unittest import main, TestCase 13 | import uredis 14 | 15 | 16 | class TestRedisConnection(TestCase): 17 | redis_test_port = 7901 18 | 19 | def setUp(self): 20 | self.redis_server = redislite.Redis(serverconfig={'port': self.redis_test_port}) 21 | self.uredis_client = uredis.Redis(host='127.0.0.1', port=self.redis_test_port) 22 | 23 | def tearDown(self): 24 | if self.redis_server: 25 | self.redis_server.shutdown() 26 | 27 | def test_hset(self): 28 | result = self.redis_server.hset("testkey", 'key', 'value') 29 | uresult = self.uredis_client.hset("testkey2", 'key', 'value') 30 | self.assertEqual(uresult, result) 31 | 32 | def test_hdel(self, *args): 33 | self.redis_server.hset("testkey", 'key', 'value') 34 | result = self.redis_server.hdel("testkey", 'key', 'value') 35 | self.redis_server.hset("testkey2", 'key', 'value') 36 | uresult = self.uredis_client.hdel("testkey2", 'key', 'value') 37 | self.assertEqual(uresult, result) 38 | 39 | def test_hexists(self, *args): 40 | self.redis_server.hset("testkey", 'key', 'value') 41 | result = self.redis_server.hexists("testkey", 'key') 42 | self.redis_server.hset("testkey2", 'key', 'value') 43 | uresult = self.uredis_client.hexists("testkey2", 'key') 44 | self.assertEqual(uresult, result) 45 | 46 | def test_hget(self, *args): 47 | self.redis_server.hset("testkey", 'key', 'value') 48 | result = self.redis_server.hget("testkey", 'key') 49 | self.redis_server.hset("testkey2", 'key', 'value') 50 | uresult = self.uredis_client.hget("testkey2", 'key') 51 | self.assertEqual(uresult, result) 52 | 53 | def test_hgetall(self, *args): 54 | self.redis_server.hset("testkey", 'key', 'value') 55 | result = self.redis_server.hgetall("testkey") 56 | self.redis_server.hset("testkey2", 'key', 'value') 57 | uresult = self.uredis_client.hgetall("testkey2") 58 | self.assertEqual(uresult, result) 59 | 60 | def test_hincrby(self, *args): 61 | self.redis_server.hset("testkey", 'key', 1) 62 | result = self.redis_server.hincrby("testkey", 'key', 1) 63 | self.redis_server.hset("testkey2", 'key', 1) 64 | uresult = self.uredis_client.hincrby("testkey2", 'key', 1) 65 | self.assertEqual(uresult, result) 66 | 67 | def test_hincrbyfloat(self, *args): 68 | self.redis_server.hset("testkey", 'key', 1.0) 69 | result = self.redis_server.hincrbyfloat("testkey", 'key', .1) 70 | self.redis_server.hset("testkey2", 'key', 1.0) 71 | uresult = self.uredis_client.hincrbyfloat("testkey2", 'key', .1) 72 | self.assertEqual(uresult, result) 73 | 74 | def test_hkeys(self, *args): 75 | self.redis_server.hset("testkey", 'key', 'value') 76 | result = self.redis_server.hkeys("testkey") 77 | uresult = self.uredis_client.hkeys("testkey") 78 | self.assertEqual(uresult, result) 79 | 80 | def test_hlen(self, *args): 81 | self.redis_server.hset("testkey", 'key', 'value') 82 | result = self.redis_server.hlen("testkey") 83 | uresult = self.uredis_client.hlen("testkey") 84 | self.assertEqual(uresult, result) 85 | 86 | def test_hmget(self, *args): 87 | self.redis_server.hset("testkey", 'key', 'value') 88 | result = self.redis_server.hmget("testkey", 'key') 89 | uresult = self.uredis_client.hmget("testkey", 'key') 90 | self.assertEqual(uresult, result) 91 | 92 | def test_hsetnx(self, *args): 93 | result = self.redis_server.hsetnx("testkey", 'key', 'value') 94 | uresult = self.uredis_client.hsetnx("testkey2", 'key', 'value') 95 | self.assertEqual(uresult, result) 96 | 97 | def hstrlen(self, *args): 98 | result = self.redis_server.hstrlen("testkey", 'key', 'value') 99 | uresult = self.uredis_client.hstrlen("testkey2", 'key', 'value') 100 | self.assertEqual(uresult, result) 101 | 102 | def hvals(self, *args): 103 | result = self.redis_server.hset("testkey", 'key', 'value') 104 | uresult = self.uredis_client.hset("testkey2", 'key', 'value') 105 | self.assertEqual(uresult, result) 106 | 107 | def hscan(self, *args): 108 | result = self.redis_server.hset("testkey", 'key', 'value') 109 | uresult = self.uredis_client.hset("testkey2", 'key', 'value') 110 | self.assertEqual(uresult, result) 111 | 112 | 113 | if __name__ == '__main__': 114 | logger = logging.getLogger('redislite.client') 115 | logger.setLevel(logging.INFO) 116 | logger.propagate = False 117 | main() 118 | -------------------------------------------------------------------------------- /tests/test_operations_lists.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | """ 3 | Tests to validate redis list functionality is working properly with micropython 4 | """ 5 | # Notes: 6 | # 7 | # 1. These tests should be run with a cpython interpreter with the redislite module installed. 8 | # 2. The micropython executable should be accesible in the path. 9 | import logging 10 | import redislite 11 | from unittest import main, TestCase 12 | import uredis 13 | 14 | 15 | class TestRedisListOperations(TestCase): 16 | redis_test_port = 7902 17 | 18 | def setUp(self): 19 | self.redis_server = redislite.Redis(serverconfig={'port': self.redis_test_port}) 20 | self.uredis_client = uredis.Redis(host='127.0.0.1', port=self.redis_test_port) 21 | 22 | def tearDown(self): 23 | self.redis_server.shutdown() 24 | self.redis_server.shutdown() 25 | 26 | def test_lrange_empty_list(self): 27 | result = self.redis_server.lrange("testlist", 0, -1) 28 | uresult = self.uredis_client.lrange("utestlist", 0, -1) 29 | self.assertEqual(uresult, result) 30 | 31 | def test_blpop_empty_list_with_timeout(self): 32 | result = self.redis_server.blpop('testlist', timeout=1) 33 | uresult = self.uredis_client.blpop('utestlist', timeout=1) 34 | self.assertEqual(uresult, result) 35 | 36 | def test_brpop_empty_list_with_timeout(self): 37 | result = self.redis_server.brpop('testlist', timeout=1) 38 | uresult = self.uredis_client.brpop('utestlist', timeout=1) 39 | self.assertEqual(uresult, result) 40 | 41 | def test_lpush_new_list_integer_value(self): 42 | result = self.redis_server.lpush('testlist', 1) 43 | uresult = self.uredis_client.lpush('utestlist', 1) 44 | self.assertEqual(uresult, result) 45 | 46 | def test_brpop(self, *args): 47 | self.redis_server.rpush('testlist', 1) 48 | result = self.redis_server.brpop('testlist', timeout=1) 49 | self.redis_server.rpush('testlist', 1) 50 | uresult = self.uredis_client.brpop('testlist', timeout=1) 51 | self.assertEqual(uresult, result) 52 | 53 | def test_brpoplpush(self, *args): 54 | self.redis_server.rpush('testlist', 1) 55 | self.redis_server.rpush('utestlist', 1) 56 | result =self.redis_server.brpoplpush('testlist', 'testlistdest', timeout=1) 57 | uresult = self.uredis_client.brpoplpush('utestlist', 'utestlistdest', timeout=1) 58 | self.assertEqual(uresult, result) 59 | result = self.redis_server.lrange('testlistdest', 0, -1) 60 | uresult = self.redis_server.lrange('utestlistdest', 0, -1) 61 | self.assertEqual(uresult, result) 62 | 63 | def test_lpop_empty(self, *args): 64 | result = self.redis_server.rpop('testlist') 65 | uresult = self.uredis_client.lpop('testlist') 66 | self.assertEqual(uresult, result) 67 | 68 | def test_lpop(self, *args): 69 | self.redis_server.rpush('testlist', 1) 70 | result = self.redis_server.lpop('testlist') 71 | self.redis_server.rpush('testlist', 1) 72 | uresult = self.uredis_client.lpop('testlist') 73 | self.assertEqual(uresult, result) 74 | 75 | def test_rpop_empty(self, *args): 76 | result = self.redis_server.rpop('testlist') 77 | uresult = self.uredis_client.rpop('testlist') 78 | self.assertEqual(uresult, result) 79 | 80 | def test_rpop(self, *args): 81 | self.redis_server.rpush('testlist', 1) 82 | result = self.redis_server.rpop('testlist') 83 | self.redis_server.rpush('testlist', 1) 84 | uresult = self.uredis_client.rpop('testlist') 85 | self.assertEqual(uresult, result) 86 | 87 | def test_rpoplpush_empty(self, *args): 88 | result = self.redis_server.rpoplpush('testlist', 'testlistdest') 89 | uresult = self.uredis_client.rpoplpush('utestlist', 'utestlistdest') 90 | self.assertEqual(uresult, result) 91 | 92 | def test_rpoplpush(self, *args): 93 | self.redis_server.rpush('testlist', 1) 94 | result = self.redis_server.rpoplpush('testlist', 'testlistdest') 95 | self.redis_server.rpush('utestlist', 1) 96 | uresult = self.uredis_client.rpoplpush('utestlist', 'utestlistdest') 97 | self.assertEqual(uresult, result) 98 | 99 | # Todo 100 | # def test_lindex(self, *args): 101 | # def test_linsert(self, *args): 102 | # def test_llen(self, *args): 103 | # def test_lpush(self, *args): 104 | # def test_lpushx(self, *args): 105 | # def test_lrange(self, *args): 106 | # def test_lrem(self, *args): 107 | # def test_lset(self, *args): 108 | # def test_ltrim(self, *args): 109 | # def test_rpush(self, *args): 110 | # def test_rpushx(self, *args): 111 | 112 | 113 | if __name__ == '__main__': 114 | logger = logging.getLogger('redislite') 115 | logger.setLevel(logging.INFO) 116 | logger.propagate = False 117 | main() 118 | -------------------------------------------------------------------------------- /tests/test_operations_set.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | """ 3 | Tests to validate redis list functionality is working properly with micropython 4 | """ 5 | # Notes: 6 | # 7 | # 1. These tests should be run with a cpython interpreter with the redislite module installed. 8 | # 2. The micropython executable should be accesible in the path. 9 | import logging 10 | import redislite 11 | from unittest import main, TestCase 12 | import uredis 13 | 14 | 15 | class TestSetOperations(TestCase): 16 | redis_test_port = 7903 17 | 18 | def setUp(self): 19 | self.redis_server = redislite.Redis(serverconfig={'port': self.redis_test_port}) 20 | self.uredis_client = uredis.Redis(host='127.0.0.1', port=self.redis_test_port) 21 | 22 | def tearDown(self): 23 | self.redis_server.shutdown() 24 | 25 | def test_sadd(self): 26 | result = self.redis_server.sadd("testset", 1) 27 | uresult = self.uredis_client.sadd("utestset", 1) 28 | self.assertEqual(uresult, result) 29 | 30 | def test_scard(self): 31 | result = self.redis_server.sadd("testset", 1) 32 | uresult = self.uredis_client.sadd("utestset", 1) 33 | result = self.redis_server.scard("testset") 34 | uresult = self.uredis_client.scard("utestset") 35 | self.assertEqual(uresult, result) 36 | 37 | def test_sdiff(self): 38 | result = self.redis_server.sadd("testset", 1, 2) 39 | result = self.redis_server.sadd("testset2", 2, 3) 40 | uresult = self.uredis_client.sadd("utestset", 1, 2) 41 | uresult = self.uredis_client.sadd("utestset2", 2, 3) 42 | 43 | result = self.redis_server.sdiff("testset", 'testset2') 44 | uresult = self.uredis_client.sdiff("utestset", 'utestset2') 45 | 46 | self.assertEqual(uresult, result) 47 | 48 | 49 | if __name__ == '__main__': 50 | logger = logging.getLogger('redislite') 51 | logger.setLevel(logging.INFO) 52 | logger.propagate = False 53 | main() 54 | -------------------------------------------------------------------------------- /tests/test_operations_sortedset.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | """ 3 | Tests to validate redis list functionality is working properly with micropython 4 | """ 5 | # Notes: 6 | # 7 | # 1. These tests should be run with a cpython interpreter with the redislite module installed. 8 | # 2. The micropython executable should be accesible in the path. 9 | import logging 10 | import redislite 11 | from unittest import main, TestCase 12 | import uredis 13 | 14 | 15 | class TestSortedSetOperations(TestCase): 16 | redis_test_port = 7905 17 | 18 | def setUp(self): 19 | self.redis_server = redislite.Redis(serverconfig={'port': self.redis_test_port}) 20 | self.uredis_client = uredis.Redis(host='127.0.0.1', port=self.redis_test_port) 21 | 22 | def tearDown(self): 23 | self.redis_server.shutdown() 24 | 25 | def test_zadd(self): 26 | result = self.redis_server.zadd("testset", 'one', 1) 27 | uresult = self.uredis_client.zadd("utestset", 'one', 1) 28 | self.assertEqual(uresult, result) 29 | 30 | def test_zcard(self): 31 | result = self.redis_server.zadd("testset", 'one', 1) 32 | uresult = self.uredis_client.zadd("utestset", 'one', 1) 33 | result = self.redis_server.zcard("testset") 34 | uresult = self.uredis_client.zcard("utestset") 35 | self.assertEqual(uresult, result) 36 | 37 | 38 | if __name__ == '__main__': 39 | logger = logging.getLogger('redislite') 40 | logger.setLevel(logging.INFO) 41 | logger.propagate = False 42 | main() 43 | -------------------------------------------------------------------------------- /tests_micropython/operations_lists.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | """ 3 | Tests to validate redis list functionality is working properly with micropython 4 | """ 5 | # Notes: 6 | # 7 | # 1. These tests should be run with a cpython interpreter with the redislite module installed. 8 | # 2. The micropython executable should be accesible in the path. 9 | import json 10 | import os 11 | import redislite 12 | import subprocess 13 | import tempfile 14 | from unittest import main, TestCase 15 | 16 | 17 | base_redis_command = """import json 18 | import sys 19 | sys.path.insert(0, '{cwd}') 20 | from uredis import Redis 21 | r=Redis(port={port}) 22 | result = None 23 | {command} 24 | r.set('command_result', json.dumps(result)) 25 | """ 26 | 27 | class TestRedisListOperations(TestCase): 28 | redis_test_port = 7899 29 | micropython_executable = 'micropython' 30 | 31 | def setUp(self): 32 | self.redis_server = redislite.Redis(serverconfig={'port': 7899}) 33 | 34 | def tearDown(self): 35 | self.redis_server.shutdown() 36 | 37 | def generate_redis_script(self, command): 38 | return base_redis_command.format( 39 | cwd=os.getcwd(), 40 | port=self.redis_test_port, 41 | command=command 42 | ) 43 | 44 | def add_line_numbers(self, output): 45 | result = '' 46 | count = 1 47 | for line in output.split('\n'): 48 | result += '%4.4d:%s\n' % (count, line.strip()) 49 | count += 1 50 | return result 51 | 52 | def micropythonRun(self, commands): 53 | """ 54 | Execute commands using the micropython interpreter 55 | 56 | Parameters 57 | ---------- 58 | commands : str 59 | The commands to run with micropython 60 | """ 61 | with tempfile.NamedTemporaryFile(suffix='.py') as script_file_handle: 62 | script_file_handle.write(commands.encode()) 63 | script_file_handle.flush() 64 | try: 65 | subprocess.check_call( 66 | [ 67 | self.micropython_executable, 68 | script_file_handle.name 69 | ] 70 | ) 71 | except subprocess.CalledProcessError as error: 72 | print(self.add_line_numbers(commands)) 73 | print(error.output) 74 | raise error('Micropython command failed') 75 | result = json.loads(self.redis_server.get('command_result').decode()) 76 | return result 77 | 78 | def test_lrange_empty_list(self): 79 | command = self.generate_redis_script('result = r.lrange("testlist", 0, -1)') 80 | micropython_result = self.micropythonRun(command) 81 | redis_py_result = self.redis_server.lrange('testlist2', 0, -1) 82 | self.assertEqual(micropython_result, redis_py_result) 83 | 84 | def test_blpop_empty_list_with_timeout(self): 85 | command = self.generate_redis_script('result = r.blpop("testlist", timeout=1)') 86 | micropython_result = self.micropythonRun(command) 87 | redis_py_result = self.redis_server.blpop('testlist2', timeout=1) 88 | self.assertEqual(micropython_result, redis_py_result) 89 | 90 | def test_lpush_new_list_integer_value(self): 91 | command = self.generate_redis_script('result = r.lpush("testlist", 1)') 92 | micropython_result = self.micropythonRun(command) 93 | redis_py_result = self.redis_server.lpush('testlist2', 1) 94 | self.assertEqual(micropython_result, redis_py_result) 95 | result = self.redis_server.lrange('testlist', 0, -1) 96 | self.assertEqual(result, [b'1']) 97 | 98 | 99 | if __name__ == '__main__': 100 | main() 101 | -------------------------------------------------------------------------------- /tox.ini: -------------------------------------------------------------------------------- 1 | [tox] 2 | skipsdist=True 3 | skip_missing_interpreters=True 4 | envlist = py35 5 | setupdir={toxinidir}/uredis 6 | 7 | [testenv] 8 | deps= 9 | nose 10 | nose-cov 11 | coveralls 12 | redislite 13 | 14 | passenv= 15 | TRAVIS_BUILD_NUMBER 16 | 17 | whitelist_externals= 18 | ci_scripts/* 19 | /bin/bash 20 | python3.5 21 | 22 | commands= 23 | python3.5 ci_scripts/setup_packages 24 | # pip install -v -f dist {toxinidir}/uredis {toxinidir}/redis.client {toxinidir}/redis.connection {toxinidir}/redis.key {toxinidir}/redis.list {toxinidir}/redis.pubsub 25 | pip install -v --no-cache-dir --no-index -v -f dist micropython-redis[all] 26 | nosetests --with-coverage --cover-erase --exe tests 27 | 28 | [testenv:build_docs] 29 | basepython=python3.5 30 | deps= 31 | sphinx 32 | sphinx-pypi-upload 33 | sphinx_rtd_theme 34 | recommonmark 35 | {toxinidir}/uredis 36 | {toxinidir}/redis.client 37 | {toxinidir}/redis.connection 38 | {toxinidir}/redis.geo 39 | {toxinidir}/redis.hash 40 | {toxinidir}/redis.key 41 | {toxinidir}/redis.list 42 | {toxinidir}/redis.pubsub 43 | {toxinidir}/redis.set 44 | {toxinidir}/redis.sortedset 45 | 46 | commands= 47 | sphinx-build -b html docs build 48 | 49 | [testenv:pylint] 50 | deps= 51 | pylint 52 | commands= 53 | pylint --output-format=parseable uredis 54 | 55 | [testenv:pep8] 56 | deps= 57 | flake8 58 | commands = 59 | flake8 {posargs} 60 | -------------------------------------------------------------------------------- /uredis/setup.py: -------------------------------------------------------------------------------- 1 | import sys 2 | # Remove current dir from sys.path, otherwise setuptools will peek up our 3 | # module instead of system. 4 | sys.path.pop(0) 5 | from setuptools import setup 6 | 7 | 8 | setup( 9 | name='micropython-redis', 10 | version='0.0.57', 11 | description='redis module for MicroPython', 12 | long_description="""This is a redis client module implemented specifically for MicroPython. 13 | 14 | As a result, this module does not support functionality not available on embedded environments and it is structured to 15 | allow operating in environments with limited resources. 16 | 17 | Note that this module is a work in progress and currently supports just a subset of all of the redis functionality 18 | 19 | Please help with the development if you are interested in this module.""", 20 | url='https://github.com/dhubbard/micropython-redis', 21 | author='Dwight Hubbard', 22 | author_email="dwight@dwighthubbard.com", 23 | install_requires=[ 24 | 'micropython-redis-modular', 25 | 'micropython-redis.client', 26 | 'micropython-redis.connection', 27 | 'micropython-redis.geo', 28 | 'micropython-redis.hash', 29 | 'micropython-redis.key', 30 | 'micropython-redis.list', 31 | 'micropython-redis.pubsub', # Pubsub works differently and isn't currently implemented 32 | 'micropython-redis.set', 33 | 'micropython-redis.sortedset', 34 | 'micropython-redis.string', 35 | ], 36 | maintainer='Dwight Hubbard', 37 | maintainer_email='dwight@dwighthubbard.com', 38 | license='MIT', 39 | packages=['uredis'], 40 | zip_safe=True, 41 | ) 42 | -------------------------------------------------------------------------------- /uredis/uredis/__init__.py: -------------------------------------------------------------------------------- 1 | __version__ = '0.0.57' 2 | 3 | from .uredis import Redis, StrictRedis 4 | -------------------------------------------------------------------------------- /uredis/uredis/uredis.py: -------------------------------------------------------------------------------- 1 | redis_components = [] 2 | try: 3 | from uredis_modular.connection import Connection 4 | redis_components.append(Connection) 5 | except ImportError: 6 | pass 7 | 8 | try: 9 | from uredis_modular.geo import Geo 10 | redis_components.append(Geo) 11 | except ImportError: 12 | pass 13 | 14 | try: 15 | from uredis_modular.hash import Hash 16 | redis_components.append(Hash) 17 | except ImportError: 18 | pass 19 | 20 | # from .hyperloglog import HyperLogLog 21 | 22 | try: 23 | from uredis_modular.key import Key 24 | redis_components.append(Key) 25 | except ImportError: 26 | pass 27 | 28 | try: 29 | from uredis_modular.list import List 30 | redis_components.append(List) 31 | except ImportError: 32 | pass 33 | 34 | try: 35 | from uredis_modular.pubsub import PubSub 36 | redis_components.append(PubSub) 37 | except ImportError: 38 | pass 39 | 40 | # from .server import Server 41 | 42 | try: 43 | from uredis_modular.set import Set 44 | redis_components.append(Set) 45 | except ImportError: 46 | pass 47 | 48 | try: 49 | from uredis_modular.sortedset import SortedSet 50 | redis_components.append(SortedSet) 51 | except ImportError: 52 | pass 53 | 54 | 55 | try: 56 | from uredis_modular.string import String 57 | redis_components.append(String) 58 | except ImportError: 59 | pass 60 | 61 | 62 | class Redis(*redis_components): 63 | """ 64 | Primary Redis Client Class. 65 | 66 | This class provides a Redis Client with all the functionality of the supported subclasses. 67 | 68 | This class is intended to be mostly compatible with the redis-py redis.Redis()/redis.StrictRedis() classes. 69 | """ 70 | pass 71 | 72 | 73 | class StrictRedis(Redis): 74 | pass 75 | -------------------------------------------------------------------------------- /uredis_modular/setup.py: -------------------------------------------------------------------------------- 1 | import sys 2 | # Remove current dir from sys.path, otherwise setuptools will peek up our 3 | # module instead of system. 4 | sys.path.pop(0) 5 | from setuptools import setup 6 | 7 | 8 | setup( 9 | name='micropython-redis-modular', 10 | version='0.0.57', 11 | description='redis module for MicroPython that allows importing only a subset of the redis functionality for low memory environments', 12 | long_description="""This is a redis client module implemented specifically for MicroPython. 13 | 14 | As a result, this module does not support functionality not available on embedded environments and it is structured to 15 | allow operating in environments with limited resources. 16 | 17 | Note that this module is a work in progress and currently supports just a subset of all of the redis functionality 18 | 19 | Please help with the development if you are interested in this module.""", 20 | url='https://github.com/dhubbard/micropython-redis', 21 | author='Dwight Hubbard', 22 | author_email="dwight@dwighthubbard.com", 23 | install_requires=[ 24 | 'micropython-redis.client', 25 | ], 26 | maintainer='Dwight Hubbard', 27 | maintainer_email='dwight@dwighthubbard.com', 28 | license='MIT', 29 | packages=['uredis_modular'], 30 | zip_safe=True, 31 | ) 32 | -------------------------------------------------------------------------------- /uredis_modular/uredis_modular/__init__.py: -------------------------------------------------------------------------------- 1 | __version__ = '0.0.57' 2 | 3 | all = ['client', 'geo', 'hash', 'key', 'list', 'pubsub', 'set', 'sortedset'] 4 | --------------------------------------------------------------------------------