├── .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 |
--------------------------------------------------------------------------------