From c161a1b183349b0f9b3c91f7dbe1605c86a3b8f0 Mon Sep 17 00:00:00 2001 From: Dave Astels Date: Sat, 23 Mar 2019 08:46:24 -0400 Subject: [PATCH 01/15] initial code commit --- .gitignore | 12 + .pylintrc | 433 ++++++++++++++++++++++++++ .readthedocs.yml | 3 + .travis.yml | 48 +++ CODE_OF_CONDUCT.md | 127 ++++++++ LICENSE | 21 ++ README.md | 1 - README.rst | 117 +++++++ adafruit_itertools.py | 515 +++++++++++++++++++++++++++++++ adafruit_itertools_extras.py | 320 +++++++++++++++++++ docs/_static/favicon.ico | Bin 0 -> 4414 bytes docs/api.rst | 11 + docs/conf.py | 153 +++++++++ docs/examples.rst | 8 + docs/index.rst | 47 +++ examples/itertools_simpletest.py | 57 ++++ requirements.txt | 1 + setup.py | 63 ++++ 18 files changed, 1936 insertions(+), 1 deletion(-) create mode 100644 .gitignore create mode 100644 .pylintrc create mode 100644 .readthedocs.yml create mode 100644 .travis.yml create mode 100644 CODE_OF_CONDUCT.md create mode 100644 LICENSE delete mode 100644 README.md create mode 100644 README.rst create mode 100644 adafruit_itertools.py create mode 100644 adafruit_itertools_extras.py create mode 100644 docs/_static/favicon.ico create mode 100644 docs/api.rst create mode 100644 docs/conf.py create mode 100644 docs/examples.rst create mode 100644 docs/index.rst create mode 100644 examples/itertools_simpletest.py create mode 100644 requirements.txt create mode 100644 setup.py diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..55f127b --- /dev/null +++ b/.gitignore @@ -0,0 +1,12 @@ +*.mpy +.idea +__pycache__ +_build +*.pyc +.env +build* +bundles +*.DS_Store +.eggs +dist +**/*.egg-info \ No newline at end of file diff --git a/.pylintrc b/.pylintrc new file mode 100644 index 0000000..039eaec --- /dev/null +++ b/.pylintrc @@ -0,0 +1,433 @@ +[MASTER] + +# A comma-separated list of package or module names from where C extensions may +# be loaded. Extensions are loading into the active Python interpreter and may +# run arbitrary code +extension-pkg-whitelist= + +# Add files or directories to the blacklist. They should be base names, not +# paths. +ignore=CVS + +# Add files or directories matching the regex patterns to the blacklist. The +# regex matches against base names, not paths. +ignore-patterns= + +# Python code to execute, usually for sys.path manipulation such as +# pygtk.require(). +#init-hook= + +# Use multiple processes to speed up Pylint. +# jobs=1 +jobs=2 + +# List of plugins (as comma separated values of python modules names) to load, +# usually to register additional checkers. +load-plugins= + +# Pickle collected data for later comparisons. +persistent=yes + +# Specify a configuration file. +#rcfile= + +# Allow loading of arbitrary C extensions. Extensions are imported into the +# active Python interpreter and may run arbitrary code. +unsafe-load-any-extension=no + + +[MESSAGES CONTROL] + +# Only show warnings with the listed confidence levels. Leave empty to show +# all. Valid levels: HIGH, INFERENCE, INFERENCE_FAILURE, UNDEFINED +confidence= + +# Disable the message, report, category or checker with the given id(s). You +# can either give multiple identifiers separated by comma (,) or put this +# option multiple times (only on the command line, not in the configuration +# file where it should appear only once).You can also use "--disable=all" to +# disable everything first and then reenable specific checks. For example, if +# you want to run only the similarities checker, you can use "--disable=all +# --enable=similarities". If you want to run only the classes checker, but have +# no Warning level messages displayed, use"--disable=all --enable=classes +# --disable=W" +# disable=import-error,print-statement,parameter-unpacking,unpacking-in-except,old-raise-syntax,backtick,long-suffix,old-ne-operator,old-octal-literal,import-star-module-level,raw-checker-failed,bad-inline-option,locally-disabled,locally-enabled,file-ignored,suppressed-message,useless-suppression,deprecated-pragma,apply-builtin,basestring-builtin,buffer-builtin,cmp-builtin,coerce-builtin,execfile-builtin,file-builtin,long-builtin,raw_input-builtin,reduce-builtin,standarderror-builtin,unicode-builtin,xrange-builtin,coerce-method,delslice-method,getslice-method,setslice-method,no-absolute-import,old-division,dict-iter-method,dict-view-method,next-method-called,metaclass-assignment,indexing-exception,raising-string,reload-builtin,oct-method,hex-method,nonzero-method,cmp-method,input-builtin,round-builtin,intern-builtin,unichr-builtin,map-builtin-not-iterating,zip-builtin-not-iterating,range-builtin-not-iterating,filter-builtin-not-iterating,using-cmp-argument,eq-without-hash,div-method,idiv-method,rdiv-method,exception-message-attribute,invalid-str-codec,sys-max-int,bad-python3-import,deprecated-string-function,deprecated-str-translate-call +disable=print-statement,parameter-unpacking,unpacking-in-except,old-raise-syntax,backtick,long-suffix,old-ne-operator,old-octal-literal,import-star-module-level,raw-checker-failed,bad-inline-option,locally-disabled,locally-enabled,file-ignored,suppressed-message,useless-suppression,deprecated-pragma,apply-builtin,basestring-builtin,buffer-builtin,cmp-builtin,coerce-builtin,execfile-builtin,file-builtin,long-builtin,raw_input-builtin,reduce-builtin,standarderror-builtin,unicode-builtin,xrange-builtin,coerce-method,delslice-method,getslice-method,setslice-method,no-absolute-import,old-division,dict-iter-method,dict-view-method,next-method-called,metaclass-assignment,indexing-exception,raising-string,reload-builtin,oct-method,hex-method,nonzero-method,cmp-method,input-builtin,round-builtin,intern-builtin,unichr-builtin,map-builtin-not-iterating,zip-builtin-not-iterating,range-builtin-not-iterating,filter-builtin-not-iterating,using-cmp-argument,eq-without-hash,div-method,idiv-method,rdiv-method,exception-message-attribute,invalid-str-codec,sys-max-int,bad-python3-import,deprecated-string-function,deprecated-str-translate-call,import-error + +# Enable the message, report, category or checker with the given id(s). You can +# either give multiple identifier separated by comma (,) or put this option +# multiple time (only on the command line, not in the configuration file where +# it should appear only once). See also the "--disable" option for examples. +enable= + + +[REPORTS] + +# Python expression which should return a note less than 10 (10 is the highest +# note). You have access to the variables errors warning, statement which +# respectively contain the number of errors / warnings messages and the total +# number of statements analyzed. This is used by the global evaluation report +# (RP0004). +evaluation=10.0 - ((float(5 * error + warning + refactor + convention) / statement) * 10) + +# Template used to display messages. This is a python new-style format string +# used to format the message information. See doc for all details +#msg-template= + +# Set the output format. Available formats are text, parseable, colorized, json +# and msvs (visual studio).You can also give a reporter class, eg +# mypackage.mymodule.MyReporterClass. +output-format=text + +# Tells whether to display a full report or only the messages +reports=no + +# Activate the evaluation score. +score=yes + + +[REFACTORING] + +# Maximum number of nested blocks for function / method body +max-nested-blocks=5 + + +[LOGGING] + +# Logging modules to check that the string format arguments are in logging +# function parameter format +logging-modules=logging + + +[SPELLING] + +# Spelling dictionary name. Available dictionaries: none. To make it working +# install python-enchant package. +spelling-dict= + +# List of comma separated words that should not be checked. +spelling-ignore-words= + +# A path to a file that contains private dictionary; one word per line. +spelling-private-dict-file= + +# Tells whether to store unknown words to indicated private dictionary in +# --spelling-private-dict-file option instead of raising a message. +spelling-store-unknown-words=no + + +[MISCELLANEOUS] + +# List of note tags to take in consideration, separated by a comma. +# notes=FIXME,XXX,TODO +notes=FIXME,XXX + + +[TYPECHECK] + +# List of decorators that produce context managers, such as +# contextlib.contextmanager. Add to this list to register other decorators that +# produce valid context managers. +contextmanager-decorators=contextlib.contextmanager + +# List of members which are set dynamically and missed by pylint inference +# system, and so shouldn't trigger E1101 when accessed. Python regular +# expressions are accepted. +generated-members= + +# Tells whether missing members accessed in mixin class should be ignored. A +# mixin class is detected if its name ends with "mixin" (case insensitive). +ignore-mixin-members=yes + +# This flag controls whether pylint should warn about no-member and similar +# checks whenever an opaque object is returned when inferring. The inference +# can return multiple potential results while evaluating a Python object, but +# some branches might not be evaluated, which results in partial inference. In +# that case, it might be useful to still emit no-member and other checks for +# the rest of the inferred objects. +ignore-on-opaque-inference=yes + +# List of class names for which member attributes should not be checked (useful +# for classes with dynamically set attributes). This supports the use of +# qualified names. +ignored-classes=optparse.Values,thread._local,_thread._local + +# List of module names for which member attributes should not be checked +# (useful for modules/projects where namespaces are manipulated during runtime +# and thus existing member attributes cannot be deduced by static analysis. It +# supports qualified module names, as well as Unix pattern matching. +ignored-modules=board + +# Show a hint with possible names when a member name was not found. The aspect +# of finding the hint is based on edit distance. +missing-member-hint=yes + +# The minimum edit distance a name should have in order to be considered a +# similar match for a missing member name. +missing-member-hint-distance=1 + +# The total number of similar names that should be taken in consideration when +# showing a hint for a missing member. +missing-member-max-choices=1 + + +[VARIABLES] + +# List of additional names supposed to be defined in builtins. Remember that +# you should avoid to define new builtins when possible. +additional-builtins= + +# Tells whether unused global variables should be treated as a violation. +allow-global-unused-variables=yes + +# List of strings which can identify a callback function by name. A callback +# name must start or end with one of those strings. +callbacks=cb_,_cb + +# A regular expression matching the name of dummy variables (i.e. expectedly +# not used). +dummy-variables-rgx=_+$|(_[a-zA-Z0-9_]*[a-zA-Z0-9]+?$)|dummy|^ignored_|^unused_ + +# Argument names that match this expression will be ignored. Default to name +# with leading underscore +ignored-argument-names=_.*|^ignored_|^unused_ + +# Tells whether we should check for unused import in __init__ files. +init-import=no + +# List of qualified module names which can have objects that can redefine +# builtins. +redefining-builtins-modules=six.moves,future.builtins + + +[FORMAT] + +# Expected format of line ending, e.g. empty (any line ending), LF or CRLF. +# expected-line-ending-format= +expected-line-ending-format=LF + +# Regexp for a line that is allowed to be longer than the limit. +ignore-long-lines=^\s*(# )??$ + +# Number of spaces of indent required inside a hanging or continued line. +indent-after-paren=4 + +# String used as indentation unit. This is usually " " (4 spaces) or "\t" (1 +# tab). +indent-string=' ' + +# Maximum number of characters on a single line. +max-line-length=100 + +# Maximum number of lines in a module +max-module-lines=1000 + +# List of optional constructs for which whitespace checking is disabled. `dict- +# separator` is used to allow tabulation in dicts, etc.: {1 : 1,\n222: 2}. +# `trailing-comma` allows a space between comma and closing bracket: (a, ). +# `empty-line` allows space-only lines. +no-space-check=trailing-comma,dict-separator + +# Allow the body of a class to be on the same line as the declaration if body +# contains single statement. +single-line-class-stmt=no + +# Allow the body of an if to be on the same line as the test if there is no +# else. +single-line-if-stmt=no + + +[SIMILARITIES] + +# Ignore comments when computing similarities. +ignore-comments=yes + +# Ignore docstrings when computing similarities. +ignore-docstrings=yes + +# Ignore imports when computing similarities. +ignore-imports=no + +# Minimum lines number of a similarity. +min-similarity-lines=4 + + +[BASIC] + +# Naming hint for argument names +argument-name-hint=(([a-z][a-z0-9_]{2,30})|(_[a-z0-9_]*))$ + +# Regular expression matching correct argument names +argument-rgx=(([a-z][a-z0-9_]{2,30})|(_[a-z0-9_]*))$ + +# Naming hint for attribute names +attr-name-hint=(([a-z][a-z0-9_]{2,30})|(_[a-z0-9_]*))$ + +# Regular expression matching correct attribute names +attr-rgx=(([a-z][a-z0-9_]{2,30})|(_[a-z0-9_]*))$ + +# Bad variable names which should always be refused, separated by a comma +bad-names=foo,bar,baz,toto,tutu,tata + +# Naming hint for class attribute names +class-attribute-name-hint=([A-Za-z_][A-Za-z0-9_]{2,30}|(__.*__))$ + +# Regular expression matching correct class attribute names +class-attribute-rgx=([A-Za-z_][A-Za-z0-9_]{2,30}|(__.*__))$ + +# Naming hint for class names +# class-name-hint=[A-Z_][a-zA-Z0-9]+$ +class-name-hint=[A-Z_][a-zA-Z0-9_]+$ + +# Regular expression matching correct class names +# class-rgx=[A-Z_][a-zA-Z0-9]+$ +class-rgx=[A-Z_][a-zA-Z0-9_]+$ + +# Naming hint for constant names +const-name-hint=(([A-Z_][A-Z0-9_]*)|(__.*__))$ + +# Regular expression matching correct constant names +const-rgx=(([A-Z_][A-Z0-9_]*)|(__.*__))$ + +# Minimum line length for functions/classes that require docstrings, shorter +# ones are exempt. +docstring-min-length=-1 + +# Naming hint for function names +function-name-hint=(([a-z][a-z0-9_]{2,30})|(_[a-z0-9_]*))$ + +# Regular expression matching correct function names +function-rgx=(([a-z][a-z0-9_]{2,30})|(_[a-z0-9_]*))$ + +# Good variable names which should always be accepted, separated by a comma +# good-names=i,j,k,ex,Run,_ +good-names=r,g,b,w,i,j,k,n,x,y,z,ex,ok,Run,_ + +# Include a hint for the correct naming format with invalid-name +include-naming-hint=no + +# Naming hint for inline iteration names +inlinevar-name-hint=[A-Za-z_][A-Za-z0-9_]*$ + +# Regular expression matching correct inline iteration names +inlinevar-rgx=[A-Za-z_][A-Za-z0-9_]*$ + +# Naming hint for method names +method-name-hint=(([a-z][a-z0-9_]{2,30})|(_[a-z0-9_]*))$ + +# Regular expression matching correct method names +method-rgx=(([a-z][a-z0-9_]{2,30})|(_[a-z0-9_]*))$ + +# Naming hint for module names +module-name-hint=(([a-z_][a-z0-9_]*)|([A-Z][a-zA-Z0-9]+))$ + +# Regular expression matching correct module names +module-rgx=(([a-z_][a-z0-9_]*)|([A-Z][a-zA-Z0-9]+))$ + +# Colon-delimited sets of names that determine each other's naming style when +# the name regexes allow several styles. +name-group= + +# Regular expression which should only match function or class names that do +# not require a docstring. +no-docstring-rgx=^_ + +# List of decorators that produce properties, such as abc.abstractproperty. Add +# to this list to register other decorators that produce valid properties. +property-classes=abc.abstractproperty + +# Naming hint for variable names +variable-name-hint=(([a-z][a-z0-9_]{2,30})|(_[a-z0-9_]*))$ + +# Regular expression matching correct variable names +variable-rgx=(([a-z][a-z0-9_]{2,30})|(_[a-z0-9_]*))$ + + +[IMPORTS] + +# Allow wildcard imports from modules that define __all__. +allow-wildcard-with-all=no + +# Analyse import fallback blocks. This can be used to support both Python 2 and +# 3 compatible code, which means that the block might have code that exists +# only in one or another interpreter, leading to false positives when analysed. +analyse-fallback-blocks=no + +# Deprecated modules which should not be used, separated by a comma +deprecated-modules=optparse,tkinter.tix + +# Create a graph of external dependencies in the given file (report RP0402 must +# not be disabled) +ext-import-graph= + +# Create a graph of every (i.e. internal and external) dependencies in the +# given file (report RP0402 must not be disabled) +import-graph= + +# Create a graph of internal dependencies in the given file (report RP0402 must +# not be disabled) +int-import-graph= + +# Force import order to recognize a module as part of the standard +# compatibility libraries. +known-standard-library= + +# Force import order to recognize a module as part of a third party library. +known-third-party=enchant + + +[CLASSES] + +# List of method names used to declare (i.e. assign) instance attributes. +defining-attr-methods=__init__,__new__,setUp + +# List of member names, which should be excluded from the protected access +# warning. +exclude-protected=_asdict,_fields,_replace,_source,_make + +# List of valid names for the first argument in a class method. +valid-classmethod-first-arg=cls + +# List of valid names for the first argument in a metaclass class method. +valid-metaclass-classmethod-first-arg=mcs + + +[DESIGN] + +# Maximum number of arguments for function / method +max-args=5 + +# Maximum number of attributes for a class (see R0902). +# max-attributes=7 +max-attributes=11 + +# Maximum number of boolean expressions in a if statement +max-bool-expr=5 + +# Maximum number of branch for function / method body +max-branches=12 + +# Maximum number of locals for function / method body +max-locals=15 + +# Maximum number of parents for a class (see R0901). +max-parents=7 + +# Maximum number of public methods for a class (see R0904). +max-public-methods=20 + +# Maximum number of return / yield for function / method body +max-returns=6 + +# Maximum number of statements in function / method body +max-statements=50 + +# Minimum number of public methods for a class (see R0903). +min-public-methods=1 + + +[EXCEPTIONS] + +# Exceptions that will emit a warning when being caught. Defaults to +# "Exception" +overgeneral-exceptions=Exception diff --git a/.readthedocs.yml b/.readthedocs.yml new file mode 100644 index 0000000..f4243ad --- /dev/null +++ b/.readthedocs.yml @@ -0,0 +1,3 @@ +python: + version: 3 +requirements_file: requirements.txt diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 0000000..87fb43a --- /dev/null +++ b/.travis.yml @@ -0,0 +1,48 @@ +# This is a common .travis.yml for generating library release zip files for +# CircuitPython library releases using circuitpython-build-tools. +# See https://github.com/adafruit/circuitpython-build-tools for detailed setup +# instructions. + +dist: xenial +language: python +python: + - "3.6" + +cache: + pip: true + +# TODO: if deployment to PyPi is desired, change 'DEPLOY_PYPI' to "true", +# or remove the env block entirely and remove the condition in the +# deploy block. +env: + - DEPLOY_PYPI="false" + +deploy: + - provider: releases + api_key: "$GITHUB_TOKEN" + file_glob: true + file: "$TRAVIS_BUILD_DIR/bundles/*" + skip_cleanup: true + overwrite: true + on: + tags: true + # TODO: Use 'travis encrypt --com -r adafruit/' to generate + # the encrypted password for adafruit-travis. Paste result below. + - provider: pypi + user: adafruit-travis + password: + secure: #-- PASTE ENCRYPTED PASSWORD HERE --# + on: + tags: true + condition: $DEPLOY_PYPI = "true" + +install: + - pip install -r requirements.txt + - pip install circuitpython-build-tools Sphinx sphinx-rtd-theme + - pip install --force-reinstall pylint==1.9.2 + +script: + - pylint adafruit_itertools.py + - ([[ ! -d "examples" ]] || pylint --disable=missing-docstring,invalid-name,bad-whitespace examples/*.py) + - circuitpython-build-bundles --filename_prefix adafruit-circuitpython-itertools --library_location . + - cd docs && sphinx-build -E -W -b html . _build/html && cd .. diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md new file mode 100644 index 0000000..8ee6e44 --- /dev/null +++ b/CODE_OF_CONDUCT.md @@ -0,0 +1,127 @@ +# Adafruit Community Code of Conduct + +## Our Pledge + +In the interest of fostering an open and welcoming environment, we as +contributors and leaders pledge to making participation in our project and +our community a harassment-free experience for everyone, regardless of age, body +size, disability, ethnicity, gender identity and expression, level or type of +experience, education, socio-economic status, nationality, personal appearance, +race, religion, or sexual identity and orientation. + +## Our Standards + +We are committed to providing a friendly, safe and welcoming environment for +all. + +Examples of behavior that contributes to creating a positive environment +include: + +* Be kind and courteous to others +* Using welcoming and inclusive language +* Being respectful of differing viewpoints and experiences +* Collaborating with other community members +* Gracefully accepting constructive criticism +* Focusing on what is best for the community +* Showing empathy towards other community members + +Examples of unacceptable behavior by participants include: + +* The use of sexualized language or imagery and sexual attention or advances +* The use of inappropriate images, including in a community member's avatar +* The use of inappropriate language, including in a community member's nickname +* Any spamming, flaming, baiting or other attention-stealing behavior +* Excessive or unwelcome helping; answering outside the scope of the question + asked +* Trolling, insulting/derogatory comments, and personal or political attacks +* Public or private harassment +* Publishing others' private information, such as a physical or electronic + address, without explicit permission +* Other conduct which could reasonably be considered inappropriate + +The goal of the standards and moderation guidelines outlined here is to build +and maintain a respectful community. We ask that you don’t just aim to be +"technically unimpeachable", but rather try to be your best self. + +We value many things beyond technical expertise, including collaboration and +supporting others within our community. Providing a positive experience for +other community members can have a much more significant impact than simply +providing the correct answer. + +## Our Responsibilities + +Project leaders are responsible for clarifying the standards of acceptable +behavior and are expected to take appropriate and fair corrective action in +response to any instances of unacceptable behavior. + +Project leaders have the right and responsibility to remove, edit, or +reject messages, comments, commits, code, issues, and other contributions +that are not aligned to this Code of Conduct, or to ban temporarily or +permanently any community member for other behaviors that they deem +inappropriate, threatening, offensive, or harmful. + +## Moderation + +Instances of behaviors that violate the Adafruit Community Code of Conduct +may be reported by any member of the community. Community members are +encouraged to report these situations, including situations they witness +involving other community members. + +You may report in the following ways: + +In any situation, you may send an email to . + +On the Adafruit Discord, you may send an open message from any channel +to all Community Helpers by tagging @community helpers. You may also send an +open message from any channel, or a direct message to @kattni#1507, +@tannewt#4653, @Dan Halbert#1614, @cater#2442, @sommersoft#0222, or +@Andon#8175. + +Email and direct message reports will be kept confidential. + +In situations on Discord where the issue is particularly egregious, possibly +illegal, requires immediate action, or violates the Discord terms of service, +you should also report the message directly to Discord. + +These are the steps for upholding our community’s standards of conduct. + +1. Any member of the community may report any situation that violates the +Adafruit Community Code of Conduct. All reports will be reviewed and +investigated. +2. If the behavior is an egregious violation, the community member who +committed the violation may be banned immediately, without warning. +3. Otherwise, moderators will first respond to such behavior with a warning. +4. Moderators follow a soft "three strikes" policy - the community member may +be given another chance, if they are receptive to the warning and change their +behavior. +5. If the community member is unreceptive or unreasonable when warned by a +moderator, or the warning goes unheeded, they may be banned for a first or +second offense. Repeated offenses will result in the community member being +banned. + +## Scope + +This Code of Conduct and the enforcement policies listed above apply to all +Adafruit Community venues. This includes but is not limited to any community +spaces (both public and private), the entire Adafruit Discord server, and +Adafruit GitHub repositories. Examples of Adafruit Community spaces include +but are not limited to meet-ups, audio chats on the Adafruit Discord, or +interaction at a conference. + +This Code of Conduct applies both within project spaces and in public spaces +when an individual is representing the project or its community. As a community +member, you are representing our community, and are expected to behave +accordingly. + +## Attribution + +This Code of Conduct is adapted from the [Contributor Covenant][homepage], +version 1.4, available at +, +and the [Rust Code of Conduct](https://www.rust-lang.org/en-US/conduct.html). + +For other projects adopting the Adafruit Community Code of +Conduct, please contact the maintainers of those projects for enforcement. +If you wish to use this code of conduct for your own project, consider +explicitly mentioning your moderation policy or making a copy with your +own moderation policy so as to avoid confusion. diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..d69195f --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2019 Dave Astels for Adafruit Industries + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/README.md b/README.md deleted file mode 100644 index 0247e5b..0000000 --- a/README.md +++ /dev/null @@ -1 +0,0 @@ -# Adafruit_CircuitPython_IterTools \ No newline at end of file diff --git a/README.rst b/README.rst new file mode 100644 index 0000000..cbf010c --- /dev/null +++ b/README.rst @@ -0,0 +1,117 @@ +Introduction +============ + +.. image:: https://readthedocs.org/projects/adafruit-circuitpython-itertools/badge/?version=latest + :target: https://circuitpython.readthedocs.io/projects/adafruit_itertools/en/latest/ + :alt: Documentation Status + +.. image:: https://img.shields.io/discord/327254708534116352.svg + :target: https://discord.gg/nBQh6qu + :alt: Discord + +.. image:: https://travis-ci.com/adafruit/Adafruit_CircuitPython_itertools.svg?branch=master + :target: https://travis-ci.com/adafruit/Adafruit_CircuitPython_itertools + :alt: Build Status + +Python's itertools for CircuitPython + + +Dependencies +============= +This driver depends on: + +* `Adafruit CircuitPython `_ + +Please ensure all dependencies are available on the CircuitPython filesystem. +This is easily achieved by downloading +`the Adafruit library and driver bundle `_. + +Installing from PyPI +-------------------- +.. note:: This library is not available on PyPI yet. Install documentation is included + as a standard element. Stay tuned for PyPI availability! +.. todo:: Remove the above note if PyPI version is/will be available at time of release. + If the library is not planned for PyPI, remove the entire 'Installing from PyPI' section. +On supported GNU/Linux systems like the Raspberry Pi, you can install the driver locally `from +PyPI `_. To install for current user: + +.. code-block:: shell + + pip3 install adafruit-circuitpython-itertools + +To install system-wide (this may be required in some cases): + +.. code-block:: shell + + sudo pip3 install adafruit-circuitpython-itertools + +To install in a virtual environment in your current project: + +.. code-block:: shell + + mkdir project-name && cd project-name + python3 -m venv .env + source .env/bin/activate + pip3 install adafruit-circuitpython-itertools + +Usage Example +============= + +.. todo:: Add a quick, simple example. It and other examples should live in the examples folder and be included in docs/examples.rst. + +Contributing +============ + +Contributions are welcome! Please read our `Code of Conduct +`_ +before contributing to help this project stay welcoming. + +Building locally +================ + +Zip release files +----------------- + +To build this library locally you'll need to install the +`circuitpython-build-tools `_ package. + +.. code-block:: shell + + python3 -m venv .env + source .env/bin/activate + pip install circuitpython-build-tools + +Once installed, make sure you are in the virtual environment: + +.. code-block:: shell + + source .env/bin/activate + +Then run the build: + +.. code-block:: shell + + circuitpython-build-bundles --filename_prefix adafruit-circuitpython-itertools --library_location . + +Sphinx documentation +----------------------- + +Sphinx is used to build the documentation based on rST files and comments in the code. First, +install dependencies (feel free to reuse the virtual environment from above): + +.. code-block:: shell + + python3 -m venv .env + source .env/bin/activate + pip install Sphinx sphinx-rtd-theme + +Now, once you have the virtual environment activated: + +.. code-block:: shell + + cd docs + sphinx-build -E -W -b html . _build/html + +This will output the documentation to ``docs/_build/html``. Open the index.html in your browser to +view them. It will also (due to -W) error out on any warning like Travis will. This is a good way to +locally verify it will pass. diff --git a/adafruit_itertools.py b/adafruit_itertools.py new file mode 100644 index 0000000..9f26c9f --- /dev/null +++ b/adafruit_itertools.py @@ -0,0 +1,515 @@ +# The MIT License (MIT) +# +# Copyright (c) 2019 Dave Astels for Adafruit Industries +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +# THE SOFTWARE. +""" +`adafruit_itertools` +================================================================================ + +Python's itertools for CircuitPython + + +* Author(s): Dave Astels + +Implementation Notes +-------------------- + +**Hardware:** + +**Software and Dependencies:** + +* Adafruit CircuitPython firmware for the supported boards: + https://github.com/adafruit/circuitpython/releases +""" +#pylint:disable=invalid-name +# imports + +__version__ = "0.0.0-auto.0" +__repo__ = "https://github.com/adafruit/Adafruit_CircuitPython_Itertools.git" + + +def accumulate(iterable, func=lambda x, y: x + y): + """Make an iterator that returns accumulated sums, or accumulated + results of other binary functions (specified via the optional func + argument). If func is supplied, it should be a function of two + arguments that returns a value. Elements of the input iterable may + be any type that can be accepted as arguments to func. (For + example, with the default operation of addition, elements may be any + addable type including Decimal or Fraction.) If the input iterable + is empty, the output iterable will also be empty. + + :param iterable: the source of values to be accumulated + :param func: the function to combine the accumulated value with the next one +""" + it = iter(iterable) + try: + acc = next(it) + except StopIteration: + return + yield acc + for element in it: + acc = func(acc, element) + yield acc + + +def chain(*iterables): + """Make an iterator that returns elements from the first iterable until it + is exhausted, then proceeds to the next iterable, until all of the iterables + are exhausted. Used for treating consecutive sequences as a single sequence. + + :param p: a list of iterable from which to yield values + + """ + # chain('ABC', 'DEF') --> A B C D E F + for i in iterables: + yield from i + + +def chain_from_iterable(iterables): + """Alternate constructor for chain(). Gets chained inputs from a + single iterable argument that is evaluated lazily. + + :param iterables: an iterable of iterables + + """ + # chain_from_iterable(['ABC', 'DEF']) --> A B C D E F + for it in iterables: + for element in it: + yield element + + +def combinations(iterable, r): + """Return r length subsequences of elements from the input iterable. + Combinations are emitted in lexicographic sort order. So, if the input + iterable is sorted, the combination tuples will be produced in sorted order. + + Elements are treated as unique based on their position, not on their value. + So if the input elements are unique, there will be no repeat values in each + combination. + + :param iterable: the iterable containing the the items to combine + :param r: the length of the resulting combinations + + """ + # combinations('ABCD', 2) --> AB AC AD BC BD CD + # combinations(range(4), 3) --> 012 013 023 123 + pool = tuple(iterable) + n = len(pool) + if r > n: + return + indices = list(range(r)) + yield tuple(pool[i] for i in indices) + while True: + for i in reversed(range(r)): + if indices[i] != i + n - r: + break + else: + return + indices[i] += 1 + for j in range(i+1, r): + indices[j] = indices[j-1] + 1 + yield tuple(pool[i] for i in indices) + + +def combinations_with_replacement(iterable, r): + """Return r length subsequences of elements from the input iterable allowing + individual elements to be repeated more than once. + + Combinations are emitted in lexicographic sort order. So, if the input + iterable is sorted, the combination tuples will be produced in sorted order. + + Elements are treated as unique based on their position, not on their value. + So if the input elements are unique, the generated combinations will also be + unique. + + :param iterable: the iterable containing the the items to combine + :param r: the length of the resulting combinations + + """ + # combinations_with_replacement('ABC', 2) --> AA AB AC BB BC CC + pool = tuple(iterable) + n = len(pool) + if not n and r: + return + indices = [0] * r + yield tuple(pool[i] for i in indices) + while True: + for i in reversed(range(r)): + if indices[i] != n - 1: + break + else: + return + indices[i:] = [indices[i] + 1] * (r - i) + yield tuple(pool[i] for i in indices) + + +def compress(data, selectors): + """Make an iterator that filters elements from data returning only those + that have a corresponding element in selectors that evaluates to True. + Stops when either the data or selectors iterables has been exhausted. + + :param data: the source of values + :param selector: the source of selection values + + """ + # compress('ABCDEF', [1,0,1,0,1,1]) --> A C E F + return (d for d, s in zip(data, selectors) if s) + + +def count(start=0, step=1): + """Make an iterator that returns evenly spaced values starting with number + start. Often used as an argument to map() to generate consecutive data + points. Also, used with zip() to add sequence numbers. + + :param start: the initial value of the sequence + :param step: how far apart subsequent values are + + """ + while True: + yield start + start += step + + +def cycle(p): + """Make an iterator returning elements from the iterable and saving a copy + of each. When the iterable is exhausted, return elements from the saved + copy. Repeats indefinitely. + + :param p: the iterable from which to yield elements + + """ + try: + len(p) + except TypeError: + # len() is not defined for this type. Assume it is + # a finite iterable so we must cache the elements. + cache = [] + for i in p: + yield i + cache.append(i) + p = cache + while p: + yield from p + + +def dropwhile(predicate, iterable): + """Make an iterator that drops elements from the iterable as long as the + predicate is true; afterwards, returns every element. Note, the iterator + does not produce any output until the predicate first becomes false, so it + may have a lengthy start-up time. + + :param predicate: used to test each element until it returns False + :param iterable: source of values + + """ + # dropwhile(lambda x: x<5, [1,4,6,4,1]) --> 6 4 1 + iterable = iter(iterable) + for x in iterable: + if not predicate(x): + yield x + break + for x in iterable: + yield x + + +def filterfalse(predicate, iterable): + """Make an iterator that filters elements from iterable returning only those + for which the predicate is False. If predicate is None, return the items + that are false. + + :param predicate: used to test each value + :param iterable: source of values + + """ + # filterfalse(lambda x: x%2, range(10)) --> 0 2 4 6 8 + if predicate is None: + predicate = bool + for x in iterable: + if not predicate(x): + yield x + + +class groupby: + """Make an iterator that returns consecutive keys and groups from the + + iterable. The key is a function computing a key value for each element. If + not specified or is None, key defaults to an identity function and returns + the element unchanged. Generally, the iterable needs to already be sorted + on the same key function. + + The operation of groupby() is similar to the uniq filter in Unix. It + generates a break or new group every time the value of the key + function changes (which is why it is usually necessary to have + sorted the data using the same key function). That behavior differs + from SQL’s GROUP BY which aggregates common elements regardless of + their input order. + + The returned group is itself an iterator that shares the underlying + iterable with groupby(). Because the source is shared, when the + groupby() object is advanced, the previous group is no longer + visible. So, if that data is needed later, it should be stored as a + list: + + groups = [] + uniquekeys = [] + data = sorted(data, key=keyfunc) + for k, g in groupby(data, keyfunc): + groups.append(list(g)) # Store group iterator as a list + uniquekeys.append(k) + + :param iterable: the source of values + :param key: the key computation function (default is None) + + """ + # [k for k, g in groupby('AAAABBBCCDAABBB')] --> A B C D A B + # [list(g) for k, g in groupby('AAAABBBCCD')] --> AAAA BBB CC D + + def __init__(self, iterable, key=None): + if key is None: + key = lambda x: x + self.keyfunc = key + self.it = iter(iterable) + self.tgtkey = self.currkey = self.currvalue = object() + + def __iter__(self): + return self + + def __next__(self): + self.id = object() + while self.currkey == self.tgtkey: + self.currvalue = next(self.it) # Exit on StopIteration + self.currkey = self.keyfunc(self.currvalue) + self.tgtkey = self.currkey + return (self.currkey, self._grouper(self.tgtkey, self.id)) + + def _grouper(self, tgtkey, id): + while self.id is id and self.currkey == tgtkey: + yield self.currvalue + try: + self.currvalue = next(self.it) + except StopIteration: + return + self.currkey = self.keyfunc(self.currvalue) + + +def islice(p, start, stop=(), step=1): + """Make an iterator that returns selected elements from the + iterable. If start is non-zero and stop is unspecified, then the + value for start is used as end, and start is taken to be 0. Thus the + supplied value specifies how many elements are to be generated, + starting the the first one.If stop is specified, then elements from + iterable are skipped until start is reached. Afterward, elements are + returned consecutively unless step is set higher than one which + results in items being skipped. If stop is None, then iteration + continues until iterable is exhausted, if at all; otherwise, it + stops at the specified position. If stop is specified and is not + None, and is not greater than start then nothing is returned. Unlike + regular slicing, islice() does not support negative values for + start, stop, or step. Can be used to extract related fields from + data where the internal structure has been flattened (for example, a + multi-line report may list a name field on every third line). + + :param p: the iterator items come from + :param start: the index of the first item + :param stop: the index one past the final item, None (the default) means + no end + :param step: how far to move to subsequent items (default is 1) + + """ + + if stop == (): + stop = start + start = 0 + # TODO: optimizing or breaking semantics? + if stop is not None and start >= stop: + return + it = iter(p) + for i in range(start): + next(it) + + while True: + yield next(it) + for i in range(step - 1): + next(it) + start += step + if stop is not None and start >= stop: + return + + +def permutations(iterable, r=None): + """Return successive r length permutations of elements in the iterable. + + If r is not specified or is None, then r defaults to the length of the + iterable and all possible full-length permutations are generated. + + Permutations are emitted in lexicographic sort order. So, if the input + iterable is sorted, the permutation tuples will be produced in sorted + order. + + Elements are treated as unique based on their position, not on their + value. So if the input elements are unique, there will be no repeat + values in each permutation. + + :param iterable: the source of values + :param r: the permutation length + + """ + # permutations('ABCD', 2) --> AB AC AD BA BC BD CA CB CD DA DB DC + # permutations(range(3)) --> 012 021 102 120 201 210 + pool = tuple(iterable) + n = len(pool) + r = n if r is None else r + if r > n: + return + indices = list(range(n)) + cycles = list(range(n, n-r, -1)) + yield tuple(pool[i] for i in indices[:r]) + while n: + for i in reversed(range(r)): + cycles[i] -= 1 + if cycles[i] == 0: + indices[i:] = indices[i+1:] + indices[i:i+1] + cycles[i] = n - i + else: + j = cycles[i] + indices[i], indices[-j] = indices[-j], indices[i] + yield tuple(pool[i] for i in indices[:r]) + break + else: + return + + +def product(*args, repeat=1): + """Cartesian product of input iterables. + + Roughly equivalent to nested for-loops in a generator expression. For + example, product(A, B) returns the same as ((x,y) for x in A for y in + B). + + The nested loops cycle like an odometer with the rightmost element + advancing on every iteration. This pattern creates a lexicographic + ordering so that if the input’s iterables are sorted, the product tuples + are emitted in sorted order. + + To compute the product of an iterable with itself, specify the number of + repetitions with the optional repeat keyword argument. For example, + product(A, repeat=4) means the same as product(A, A, A, A). + + :param args: sources of values + :param repeat: number of times to duplicate the (single) arg for taking a + product with itself (default is 1) + + """ + # product('ABCD', 'xy') --> Ax Ay Bx By Cx Cy Dx Dy + # product(range(2), repeat=3) --> 000 001 010 011 100 101 110 111 + pools = [tuple(pool) for pool in args] * repeat + result = [[]] + for pool in pools: + result = [x+[y] for x in result for y in pool] + for prod in result: + yield tuple(prod) + + +def repeat(el, n=None): + """Make an iterator that returns object over and over again. Runs + indefinitely unless the times argument is specified. Used as argument to + map() for invariant parameters to the called function. Also used with zip() + to create an invariant part of a tuple record. + + :param el: the object to yield + :param n: the number of time to yield, None (the default) means infinitely. + + """ + if n is None: + while True: + yield el + else: + for i in range(n): + yield el + + +def starmap(function, iterable): + """Make an iterator that computes the function using arguments obtained from + the iterable. Used instead of map() when argument parameters are already + grouped in tuples from a single iterable (the data has been “pre-zipped”). + The difference between map() and starmap() parallels the distinction between + function(a,b) and function(*c). + + :param function: the function to apply + :param iterable: where groups of arguments come from + + """ + for args in iterable: + yield function(*args) + + +def takewhile(predicate, iterable): + """Make an iterator that returns elements from the iterable as long + as the predicate is true. + + :param predicate: used to test values + :param iterable: source of values + + """ + # takewhile(lambda x: x<5, [1,4,6,4,1]) --> 1 4 + for x in iterable: + if predicate(x): + yield x + else: + break + + +def tee(iterable, n=2): + """Return n independent iterators from a single iterable. + + :param iterable: the iterator from which to make iterators. + :param n: the number of iterators to make (default is 2) + + """ + return [iter(iterable) for _ in range(n)] + + +def zip_longest(*args, fillvalue=None): + """Make an iterator that aggregates elements from each of the + iterables. If the iterables are of uneven length, missing values are + filled-in with fillvalue. Iteration continues until the longest + iterable is exhausted. + + :param args: the iterables to combine + :param fillvalue: value to fill in those missing from shorter iterables + """ + # zip_longest('ABCD', 'xy', fillvalue='-') --> Ax By C- D- + iterators = [iter(it) for it in args] + num_active = len(iterators) + if not num_active: + return + while True: + values = [] + for i, it in enumerate(iterators): + try: + value = next(it) + except StopIteration: + num_active -= 1 + if not num_active: + return + iterators[i] = repeat(fillvalue) + value = fillvalue + values.append(value) + yield tuple(values) diff --git a/adafruit_itertools_extras.py b/adafruit_itertools_extras.py new file mode 100644 index 0000000..d846122 --- /dev/null +++ b/adafruit_itertools_extras.py @@ -0,0 +1,320 @@ +# The MIT License (MIT) +# +# Copyright (c) 2019 Dave Astels for Adafruit Industries +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +# THE SOFTWARE. +""" +`adafruit_itertools_extras` +================================================================================ + +Extras for itertools for CircuitPython + +This module contains an extended toolset using the existing itertools as +building blocks. + +The extended tools offer the same high performance as the underlying +toolset. The superior memory performance is kept by processing elements one at +a time rather than bringing the whole iterable into memory all at once. Code +volume is kept small by linking the tools together in a functional style which +helps eliminate temporary variables. High speed is retained by preferring +"vectorized" building blocks over the use of for-loops and generators which +incur interpreter overhead. + + +* Author(s): Dave Astels + +Implementation Notes +-------------------- + +Based on code from the offical Python documentation. + +**Hardware:** + +**Software and Dependencies:** + +* Adafruit's CircuitPython port of itertools +* Adafruit CircuitPython firmware for the supported boards: + https://github.com/adafruit/circuitpython/releases +""" + +#pylint:disable=invalid-name,deprecated-lambda,keyword-arg-before-vararg + +import adafruit_itertools as it + +__version__ = "0.0.0-auto.0" +__repo__ = "https://github.com/adafruit/Adafruit_CircuitPython_Itertools.git" + + +def all_equal(iterable): + """Returns True if all the elements are equal to each other. + + :param iterable: source of values + + """ + g = it.groupby(iterable) + next(g) # should succeed, value isn't relevant + try: + next(g) # should fail: only 1 group + return False + except StopIteration: + return True + + +def dotproduct(vec1, vec2): + """Compute the dot product of two vectors. + + :param vec1: the first vector + :param vec2: the second vector + + """ + # dotproduct([1, 2, 3], [1, 2, 3]) -> 14 + return sum(map(lambda x, y: x * y, vec1, vec2)) + + +def first_true(iterable, default=False, pred=None): + """Returns the first true value in the iterable. + + If no true value is found, returns *default* + + If *pred* is not None, returns the first item for which pred(item) + is true. + + :param iterable: source of values + :param default: the value to return if no true value is found (default is + False) + :param pred: if not None test the result of applying pred to each value + instead of the values themselves (default is None) + + """ + # first_true([a,b,c], x) --> a or b or c or x + # first_true([a,b], x, f) --> a if f(a) else b if f(b) else x + try: + return next(filter(pred, iterable)) + except StopIteration: + return default + + +def flatten(iterable_of_iterables): + """Flatten one level of nesting. + + :param iterable_of_iterables: a sequence of iterables to flatten + + """ + # flatten(['ABC', 'DEF']) --> A B C D E F + return it.chain_from_iterable(iterable_of_iterables) + + +def grouper(iterable, n, fillvalue=None): + """Collect data into fixed-length chunks or blocks. + + :param iterable: source of values + :param n: chunk size + :param fillvalue: value to use for filling out the final chunk. + Defaults to None. + + """ + # grouper('ABCDEFG', 3, 'x') --> ABC DEF Gxx + args = [iter(iterable)] * n + return it.zip_longest(*args, fillvalue=fillvalue) + + +def iter_except(func, exception): + """ Call a function repeatedly, yielding the results, until exception is raised. + + Converts a call-until-exception interface to an iterator interface. + Like builtins.iter(func, sentinel) but uses an exception instead + of a sentinel to end the loop. + + Examples: + iter_except(functools.partial(heappop, h), IndexError) # priority queue iterator + iter_except(d.popitem, KeyError) # non-blocking dict iterator + iter_except(d.popleft, IndexError) # non-blocking deque iterator + iter_except(q.get_nowait, Queue.Empty) # loop over a producer Queue + iter_except(s.pop, KeyError) # non-blocking set iterator + + :param func: the function to call repeatedly + :param exception: the exception upon which to stop + + """ + try: + while True: + yield func() + except exception: + pass + + +def ncycles(iterable, n): + """Returns the sequence elements a number of times. + + :param iterable: the source of values + :param n: how many time to repeal the values + + """ + return it.chain_from_iterable(it.repeat(tuple(iterable), n)) + + +def nth(iterable, n, default=None): + """Returns the nth item or a default value. + + :param iterable: the source of values + :param n: the index of the item to fetch, starts at 0 + + """ + try: + return next(it.islice(iterable, n, n+1)) + except StopIteration: + return default + +def padnone(iterable): + """Returns the sequence elements and then returns None indefinitely. + + Useful for emulating the behavior of the built-in map() function. + + :param iterable: the source of initial values + """ + # take(5, padnone([1, 2, 3])) -> 1 2 3 None None + return it.chain(iterable, it.repeat(None)) + + +def pairwise(iterable): + """Pair up valuesin the iterable. + + :param iterable: source of values + + """ + # pairwise(range(11)) -> (1, 2), (3, 4), (5, 6), (7, 8), (9, 10) + a, b = it.tee(iterable) + try: + next(b) + except StopIteration: + pass + return zip(a, b) + + +def partition(pred, iterable): + """Use a predicate to partition entries into false entries and true entries. + + :param pred: the predicate that divides the values + :param iterable: source of values + + """ + # partition(lambda x: x % 2, range(10)) --> 0 2 4 6 8 and 1 3 5 7 9 + t1, t2 = it.tee(iterable) + return it.filterfalse(pred, t1), filter(pred, t2) + + +def prepend(value, iterator): + """Prepend a single value in front of an iterator + + :param value: the value to prepend + :param iterator: the iterator to which to prepend + + """ + # prepend(1, [2, 3, 4]) -> 1 2 3 4 + return it.chain([value], iterator) + + +def quantify(iterable, pred=bool): + """Count how many times the predicate is true. + + :param iterable: source of values + :param pred: the predicate whose result is to be quantified when applied to + all values in iterable. Defaults to bool() + + """ + # quantify([2, 56, 3, 10, 85], lambda x: x >= 10) -> 3 + return sum(map(pred, iterable)) + + +def repeatfunc(func, times=None, *args): + """Repeat calls to func with specified arguments. + + Example: repeatfunc(random.random) + + :param func: the function to be called + :param times: the number of times to call it: size of the resulting iterable + None means infinitely. Default is None. + + """ + if times is None: + return it.starmap(func, it.repeat(args)) + return it.starmap(func, it.repeat(args, times)) + + +def roundrobin(*iterables): + """Return an iterable created by repeatedly picking value from each + argument in order. + + :param args: the iterables to pick from + + """ + # roundrobin('ABC', 'D', 'EF') --> A D E B F C + # Recipe credited to George Sakkis + num_active = len(iterables) + nexts = it.cycle(iter(it).__next__ for it in iterables) + while num_active: + try: + for n in nexts: + yield n() + except StopIteration: + # Remove the iterator we just exhausted from the cycle. + num_active -= 1 + nexts = it.cycle(it.islice(nexts, num_active)) + + +def tabulate(function, start=0): + """Apply a function to a sequence of consecutive integers. + + :param function: the function of one integer argument + :param start: optional value to start at (default is 0) + + """ + # take(5, tabulate(lambda x: x * x))) -> 0 1 4 9 16 + return map(function, it.count(start)) + + +def tail(n, iterable): + """Return an iterator over the last n items + + :param n: how many values to return + :param iterable: the source of values + + """ + # tail(3, 'ABCDEFG') --> E F G + i = iter(iterable) + buf = [] + while True: + try: + buf.append(next(i)) + if len(buf) > n: + buf.pop(0) + except StopIteration: + break + return iter(buf) + + +def take(n, iterable): + """Return first n items of the iterable as a list + + :param n: how many values to take + :param iterable: the source of values + + """ + # take(3, 'ABCDEF')) -> A B C + return list(it.islice(iterable, n)) diff --git a/docs/_static/favicon.ico b/docs/_static/favicon.ico new file mode 100644 index 0000000000000000000000000000000000000000..5aca98376a1f7e593ebd9cf41a808512c2135635 GIT binary patch literal 4414 zcmd^BX;4#F6n=SG-XmlONeGrD5E6J{RVh+e928U#MG!$jWvO+UsvWh`x&VqGNx*en zx=qox7Dqv{kPwo%fZC$dDwVpRtz{HzTkSs8QhG0)%Y=-3@Kt!4ag|JcIo?$-F|?bXVS9UDUyev>MVZQ(H8K4#;BQW-t2CPorj8^KJrMX}QK zp+e<;4ldpXz~=)2GxNy811&)gt-}Q*yVQpsxr@VMoA##{)$1~=bZ1MmjeFw?uT(`8 z^g=09<=zW%r%buwN%iHtuKSg|+r7HkT0PYN*_u9k1;^Ss-Z!RBfJ?Un4w(awqp2b3 z%+myoFis_lTlCrGx2z$0BQdh+7?!JK#9K9@Z!VrG zNj6gK5r(b4?YDOLw|DPRoN7bdP{(>GEG41YcN~4r_SUHU2hgVtUwZG@s%edC;k7Sn zC)RvEnlq~raE2mY2ko64^m1KQL}3riixh?#J{o)IT+K-RdHae2eRX91-+g!y`8^># z-zI0ir>P%Xon)!@xp-BK2bDYUB9k613NRrY6%lVjbFcQc*pRqiK~8xtkNPLxt}e?&QsTB}^!39t_%Qb)~Ukn0O%iC;zt z<&A-y;3h++)>c1br`5VFM~5(83!HKx$L+my8sW_c#@x*|*vB1yU)_dt3vH;2hqPWx zAl^6@?ipx&U7pf`a*>Yq6C85nb+B=Fnn+(id$W#WB^uHAcZVG`qg;rWB}ubvi(Y>D z$ei>REw$#xp0SHAd^|1hq&9HJ=jKK8^zTH~nk)G?yUcmTh9vUM6Y0LMw4(gYVY$D$ zGl&WY&H<)BbJ&3sYbKjx1j^=3-0Q#f^}(aP1?8^`&FUWMp|rmtpK)bLQ1Zo?^s4jqK=Lfg*9&geMGVQ z#^-*!V`fG@;H&{M9S8%+;|h&Qrxym0Ar>WT4BCVLR8cGXF=JmEYN(sNT(9vl+S|%g z8r7nXQ(95i^`=+XHo|){$vf2$?=`F$^&wFlYXyXg$B{a>$-Fp+V}+D;9k=~Xl~?C4 zAB-;RKXdUzBJE{V&d&%R>aEfFe;vxqI$0@hwVM}gFeQR@j}a>DDxR+n+-*6|_)k%% z*mSpDV|=5I9!&VC&9tD%fcVygWZV!iIo2qFtm#!*(s|@ZT33*Ad;+<|3^+yrp*;oH zBSYLV(H1zTU?2WjrCQoQW)Z>J2a=dTriuvezBmu16`tM2fm7Q@d4^iqII-xFpwHGI zn9CL}QE*1vdj2PX{PIuqOe5dracsciH6OlAZATvE8rj6ykqdIjal2 z0S0S~PwHb-5?OQ-tU-^KTG@XNrEVSvo|HIP?H;7ZhYeZkhSqh-{reE!5di;1zk$#Y zCe7rOnlzFYJ6Z#Hm$GoidKB=2HBCwm`BbZVeZY4ukmG%1uz7p2URs6c9j-Gjj^oQV zsdDb3@k2e`C$1I5ML5U0Qs0C1GAp^?!*`=|Nm(vWz3j*j*8ucum2;r0^-6Aca=Gv) zc%}&;!+_*S2tlnnJnz0EKeRmw-Y!@9ob!XQBwiv}^u9MkaXHvM=!<3YX;+2#5Cj5pp?FEK750S3BgeSDtaE^ zXUM@xoV6yBFKfzvY20V&Lr0yC + +.. toctree:: + :caption: Related Products + +.. toctree:: + :caption: Other Links + + Download + CircuitPython Reference Documentation + CircuitPython Support Forum + Discord Chat + Adafruit Learning System + Adafruit Blog + Adafruit Store + +Indices and tables +================== + +* :ref:`genindex` +* :ref:`modindex` +* :ref:`search` diff --git a/examples/itertools_simpletest.py b/examples/itertools_simpletest.py new file mode 100644 index 0000000..1e3cb16 --- /dev/null +++ b/examples/itertools_simpletest.py @@ -0,0 +1,57 @@ +# The MIT License (MIT) +# +# Copyright (c) 2019 Dave Astels for Adafruit Industries +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +# THE SOFTWARE. + +import time +import board +import busio +import adafruit_si7021 +from adafruit_itertools import count +from adafruit_itertools_extras import repeatfunc, roundrobin + + +i2c = busio.I2C(board.SCL, board.SDA) +sensor = adafruit_si7021.SI7021(i2c) + + +def read_temperature(): + return sensor.temperature + + +def now(): + return time.monotonic() + + +# def non_iter(): +# reading_number = 1 + +# while True: +# datapoint = (count, now(), int(read_temperature())) +# print(datapoint) +# count += 1 +# time.sleep(20.0) + +def iter(): + datapoints = zip(count(1), repeatfunc(now), map(int, repeatfunc(read_temperature))) + + while True: + print(next(datapoints)) + time.sleep(20.0) diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..edf9394 --- /dev/null +++ b/requirements.txt @@ -0,0 +1 @@ +Adafruit-Blinka diff --git a/setup.py b/setup.py new file mode 100644 index 0000000..0e4c51a --- /dev/null +++ b/setup.py @@ -0,0 +1,63 @@ +"""A setuptools based setup module. + +See: +https://packaging.python.org/en/latest/distributing.html +https://github.com/pypa/sampleproject +""" + +from setuptools import setup, find_packages +# To use a consistent encoding +from codecs import open +from os import path + +here = path.abspath(path.dirname(__file__)) + +# Get the long description from the README file +with open(path.join(here, 'README.rst'), encoding='utf-8') as f: + long_description = f.read() + +setup( + name='adafruit-circuitpython-itertools', + + use_scm_version=True, + setup_requires=['setuptools_scm'], + + description='Python's itertools for CircuitPython', + long_description=long_description, + long_description_content_type='text/x-rst', + + # The project's main homepage. + url='https://github.com/adafruit/Adafruit_CircuitPython_Itertools', + + # Author details + author='Adafruit Industries', + author_email='circuitpython@adafruit.com', + + install_requires=[ + 'Adafruit-Blinka' + ], + + # Choose your license + license='MIT', + + # See https://pypi.python.org/pypi?%3Aaction=list_classifiers + classifiers=[ + 'Development Status :: 3 - Alpha', + 'Intended Audience :: Developers', + 'Topic :: Software Development :: Libraries', + 'Topic :: System :: Hardware', + 'License :: OSI Approved :: MIT License', + 'Programming Language :: Python :: 3', + 'Programming Language :: Python :: 3.4', + 'Programming Language :: Python :: 3.5', + ], + + # What does your project relate to? + keywords='adafruit blinka circuitpython micropython itertools programming tools', + + # You can just specify the packages manually here if your project is + # simple. Or you can use find_packages(). + # TODO: IF LIBRARY FILES ARE A PACKAGE FOLDER, + # CHANGE `py_modules=['...']` TO `packages=['...']` + py_modules=['adafruit_itertools', 'adafruit_itertools_extras'], +) From e5d1cab00211a301a88343f89e5b8acc3dd94785 Mon Sep 17 00:00:00 2001 From: Dave Astels Date: Sat, 23 Mar 2019 09:00:36 -0400 Subject: [PATCH 02/15] Add adafruit_itertools_extras to linting --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 87fb43a..6c10a07 100644 --- a/.travis.yml +++ b/.travis.yml @@ -42,7 +42,7 @@ install: - pip install --force-reinstall pylint==1.9.2 script: - - pylint adafruit_itertools.py + - pylint adafruit_itertools.py adafruit_itertools_extras.py - ([[ ! -d "examples" ]] || pylint --disable=missing-docstring,invalid-name,bad-whitespace examples/*.py) - circuitpython-build-bundles --filename_prefix adafruit-circuitpython-itertools --library_location . - cd docs && sphinx-build -E -W -b html . _build/html && cd .. From 36ed115f7d6b6c2828f8d4eeb8a1a6f252787f0c Mon Sep 17 00:00:00 2001 From: Dave Astels Date: Sat, 23 Mar 2019 09:00:56 -0400 Subject: [PATCH 03/15] Make pylint prompted changes --- adafruit_itertools.py | 18 +++++++++++------- examples/itertools_simpletest.py | 18 ++++-------------- 2 files changed, 15 insertions(+), 21 deletions(-) diff --git a/adafruit_itertools.py b/adafruit_itertools.py index 9f26c9f..4cb123a 100644 --- a/adafruit_itertools.py +++ b/adafruit_itertools.py @@ -38,8 +38,8 @@ * Adafruit CircuitPython firmware for the supported boards: https://github.com/adafruit/circuitpython/releases """ -#pylint:disable=invalid-name -# imports +#pylint:disable=invalid-name,redefined-builtin,attribute-defined-outside-init +#pyliny:disable=stop-iteration-return __version__ = "0.0.0-auto.0" __repo__ = "https://github.com/adafruit/Adafruit_CircuitPython_Itertools.git" @@ -117,13 +117,15 @@ def combinations(iterable, r): indices = list(range(r)) yield tuple(pool[i] for i in indices) while True: + index = 0 for i in reversed(range(r)): if indices[i] != i + n - r: + index = i break else: return - indices[i] += 1 - for j in range(i+1, r): + indices[index] += 1 + for j in range(index+1, r): indices[j] = indices[j-1] + 1 yield tuple(pool[i] for i in indices) @@ -151,12 +153,14 @@ def combinations_with_replacement(iterable, r): indices = [0] * r yield tuple(pool[i] for i in indices) while True: + index = 0 for i in reversed(range(r)): if indices[i] != n - 1: + index = i break else: return - indices[i:] = [indices[i] + 1] * (r - i) + indices[index:] = [indices[index] + 1] * (r - index) yield tuple(pool[i] for i in indices) @@ -341,12 +345,12 @@ def islice(p, start, stop=(), step=1): if stop is not None and start >= stop: return it = iter(p) - for i in range(start): + for _ in range(start): next(it) while True: yield next(it) - for i in range(step - 1): + for _ in range(step - 1): next(it) start += step if stop is not None and start >= stop: diff --git a/examples/itertools_simpletest.py b/examples/itertools_simpletest.py index 1e3cb16..18f3fb0 100644 --- a/examples/itertools_simpletest.py +++ b/examples/itertools_simpletest.py @@ -40,18 +40,8 @@ def now(): return time.monotonic() -# def non_iter(): -# reading_number = 1 +datapoints = zip(count(1), repeatfunc(now), map(int, repeatfunc(read_temperature))) -# while True: -# datapoint = (count, now(), int(read_temperature())) -# print(datapoint) -# count += 1 -# time.sleep(20.0) - -def iter(): - datapoints = zip(count(1), repeatfunc(now), map(int, repeatfunc(read_temperature))) - - while True: - print(next(datapoints)) - time.sleep(20.0) +while True: + print(next(datapoints)) + time.sleep(20.0) From 4c7ad423a7d444102cc777b344340cefa2e147e6 Mon Sep 17 00:00:00 2001 From: Dave Astels Date: Sat, 23 Mar 2019 09:15:26 -0400 Subject: [PATCH 04/15] Make pylint based tweaks --- adafruit_itertools.py | 12 ++++++------ examples/itertools_simpletest.py | 2 +- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/adafruit_itertools.py b/adafruit_itertools.py index 4cb123a..38289fa 100644 --- a/adafruit_itertools.py +++ b/adafruit_itertools.py @@ -39,7 +39,7 @@ https://github.com/adafruit/circuitpython/releases """ #pylint:disable=invalid-name,redefined-builtin,attribute-defined-outside-init -#pyliny:disable=stop-iteration-return +#pylint:disable=stop-iteration-return __version__ = "0.0.0-auto.0" __repo__ = "https://github.com/adafruit/Adafruit_CircuitPython_Itertools.git" @@ -400,7 +400,7 @@ def permutations(iterable, r=None): return -def product(*args, repeat=1): +def product(*args, r=1): """Cartesian product of input iterables. Roughly equivalent to nested for-loops in a generator expression. For @@ -417,13 +417,13 @@ def product(*args, repeat=1): product(A, repeat=4) means the same as product(A, A, A, A). :param args: sources of values - :param repeat: number of times to duplicate the (single) arg for taking a - product with itself (default is 1) + :param : number of times to duplicate the (single) arg for taking a + product with itself (default is 1) """ # product('ABCD', 'xy') --> Ax Ay Bx By Cx Cy Dx Dy # product(range(2), repeat=3) --> 000 001 010 011 100 101 110 111 - pools = [tuple(pool) for pool in args] * repeat + pools = [tuple(pool) for pool in args] * r result = [[]] for pool in pools: result = [x+[y] for x in result for y in pool] @@ -445,7 +445,7 @@ def repeat(el, n=None): while True: yield el else: - for i in range(n): + for _ in range(n): yield el diff --git a/examples/itertools_simpletest.py b/examples/itertools_simpletest.py index 18f3fb0..871f893 100644 --- a/examples/itertools_simpletest.py +++ b/examples/itertools_simpletest.py @@ -25,7 +25,7 @@ import busio import adafruit_si7021 from adafruit_itertools import count -from adafruit_itertools_extras import repeatfunc, roundrobin +from adafruit_itertools_extras import repeatfunc i2c = busio.I2C(board.SCL, board.SDA) From b69e8d606779572042e5b1cf27dca66f09baa6dd Mon Sep 17 00:00:00 2001 From: Dave Astels Date: Sat, 23 Mar 2019 09:23:47 -0400 Subject: [PATCH 05/15] Move to a directory --- adafruit_itertools.py => adafruit_itertools/adafruit_itertools.py | 0 .../adafruit_itertools_extras.py | 0 2 files changed, 0 insertions(+), 0 deletions(-) rename adafruit_itertools.py => adafruit_itertools/adafruit_itertools.py (100%) rename adafruit_itertools_extras.py => adafruit_itertools/adafruit_itertools_extras.py (100%) diff --git a/adafruit_itertools.py b/adafruit_itertools/adafruit_itertools.py similarity index 100% rename from adafruit_itertools.py rename to adafruit_itertools/adafruit_itertools.py diff --git a/adafruit_itertools_extras.py b/adafruit_itertools/adafruit_itertools_extras.py similarity index 100% rename from adafruit_itertools_extras.py rename to adafruit_itertools/adafruit_itertools_extras.py From 009222e6e634346a0b3b98fd113e1485355bfc0b Mon Sep 17 00:00:00 2001 From: Dave Astels Date: Sat, 23 Mar 2019 09:26:03 -0400 Subject: [PATCH 06/15] Use source files in a directory --- .travis.yml | 2 +- docs/api.rst | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.travis.yml b/.travis.yml index 6c10a07..1d09583 100644 --- a/.travis.yml +++ b/.travis.yml @@ -42,7 +42,7 @@ install: - pip install --force-reinstall pylint==1.9.2 script: - - pylint adafruit_itertools.py adafruit_itertools_extras.py + - pylint adafruit_itertools/adafruit_itertools.py adafruit_itertools/adafruit_itertools_extras.py - ([[ ! -d "examples" ]] || pylint --disable=missing-docstring,invalid-name,bad-whitespace examples/*.py) - circuitpython-build-bundles --filename_prefix adafruit-circuitpython-itertools --library_location . - cd docs && sphinx-build -E -W -b html . _build/html && cd .. diff --git a/docs/api.rst b/docs/api.rst index 2bcc440..890013e 100644 --- a/docs/api.rst +++ b/docs/api.rst @@ -4,8 +4,8 @@ .. If your library file(s) are nested in a directory (e.g. /adafruit_foo/foo.py) .. use this format as the module name: "adafruit_foo.foo" -.. automodule:: adafruit_itertools +.. automodule:: adafruit_itertools.adafruit_itertools :members: -.. automodule:: adafruit_itertools_extras +.. automodule:: adafruit_itertools.adafruit_itertools_extras :members: From c142bd0abcac8c2900ce8e97e936adee3b0bfd4a Mon Sep 17 00:00:00 2001 From: Dave Astels Date: Sat, 23 Mar 2019 09:31:06 -0400 Subject: [PATCH 07/15] Tweak source location for build --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 1d09583..294324e 100644 --- a/.travis.yml +++ b/.travis.yml @@ -44,5 +44,5 @@ install: script: - pylint adafruit_itertools/adafruit_itertools.py adafruit_itertools/adafruit_itertools_extras.py - ([[ ! -d "examples" ]] || pylint --disable=missing-docstring,invalid-name,bad-whitespace examples/*.py) - - circuitpython-build-bundles --filename_prefix adafruit-circuitpython-itertools --library_location . + - circuitpython-build-bundles --filename_prefix adafruit-circuitpython-itertools --library_location ./adafruit_itertools - cd docs && sphinx-build -E -W -b html . _build/html && cd .. From 40baff14ca66ebc4f0330e9538f8ffd3d11b0409 Mon Sep 17 00:00:00 2001 From: Dave Astels Date: Sat, 23 Mar 2019 09:34:16 -0400 Subject: [PATCH 08/15] prefix library modules --- examples/itertools_simpletest.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/examples/itertools_simpletest.py b/examples/itertools_simpletest.py index 871f893..d2a5b77 100644 --- a/examples/itertools_simpletest.py +++ b/examples/itertools_simpletest.py @@ -24,8 +24,8 @@ import board import busio import adafruit_si7021 -from adafruit_itertools import count -from adafruit_itertools_extras import repeatfunc +from adafruit_itertools.adafruit_itertools import count +from adafruit_itertools.adafruit_itertools_extras import repeatfunc i2c = busio.I2C(board.SCL, board.SDA) From 77cda4fb3cf52886ad2db865eda3bf44ef52e34c Mon Sep 17 00:00:00 2001 From: Dave Astels Date: Sat, 23 Mar 2019 10:16:07 -0400 Subject: [PATCH 09/15] More tweaks --- .travis.yml | 2 +- adafruit_itertools/adafruit_itertools.py | 9 +-------- 2 files changed, 2 insertions(+), 9 deletions(-) diff --git a/.travis.yml b/.travis.yml index 294324e..1d09583 100644 --- a/.travis.yml +++ b/.travis.yml @@ -44,5 +44,5 @@ install: script: - pylint adafruit_itertools/adafruit_itertools.py adafruit_itertools/adafruit_itertools_extras.py - ([[ ! -d "examples" ]] || pylint --disable=missing-docstring,invalid-name,bad-whitespace examples/*.py) - - circuitpython-build-bundles --filename_prefix adafruit-circuitpython-itertools --library_location ./adafruit_itertools + - circuitpython-build-bundles --filename_prefix adafruit-circuitpython-itertools --library_location . - cd docs && sphinx-build -E -W -b html . _build/html && cd .. diff --git a/adafruit_itertools/adafruit_itertools.py b/adafruit_itertools/adafruit_itertools.py index 38289fa..cf96a83 100644 --- a/adafruit_itertools/adafruit_itertools.py +++ b/adafruit_itertools/adafruit_itertools.py @@ -269,14 +269,7 @@ class groupby: iterable with groupby(). Because the source is shared, when the groupby() object is advanced, the previous group is no longer visible. So, if that data is needed later, it should be stored as a - list: - - groups = [] - uniquekeys = [] - data = sorted(data, key=keyfunc) - for k, g in groupby(data, keyfunc): - groups.append(list(g)) # Store group iterator as a list - uniquekeys.append(k) + list. :param iterable: the source of values :param key: the key computation function (default is None) From d476c63a0957144bbf8689e9fe019a31190f1ee6 Mon Sep 17 00:00:00 2001 From: Dave Astels Date: Sat, 23 Mar 2019 10:32:13 -0400 Subject: [PATCH 10/15] Fix docstring --- adafruit_itertools/adafruit_itertools.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/adafruit_itertools/adafruit_itertools.py b/adafruit_itertools/adafruit_itertools.py index cf96a83..89517e7 100644 --- a/adafruit_itertools/adafruit_itertools.py +++ b/adafruit_itertools/adafruit_itertools.py @@ -410,8 +410,8 @@ def product(*args, r=1): product(A, repeat=4) means the same as product(A, A, A, A). :param args: sources of values - :param : number of times to duplicate the (single) arg for taking a - product with itself (default is 1) + :param r: number of times to duplicate the (single) arg for taking a + product with itself (default is 1) """ # product('ABCD', 'xy') --> Ax Ay Bx By Cx Cy Dx Dy From cc23c47d6c255dba76e1b86aaa2657aa50a1cbce Mon Sep 17 00:00:00 2001 From: Dave Astels Date: Sat, 23 Mar 2019 10:39:26 -0400 Subject: [PATCH 11/15] Escape the * --- adafruit_itertools/adafruit_itertools.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/adafruit_itertools/adafruit_itertools.py b/adafruit_itertools/adafruit_itertools.py index 89517e7..ac53d7a 100644 --- a/adafruit_itertools/adafruit_itertools.py +++ b/adafruit_itertools/adafruit_itertools.py @@ -447,7 +447,7 @@ def starmap(function, iterable): the iterable. Used instead of map() when argument parameters are already grouped in tuples from a single iterable (the data has been “pre-zipped”). The difference between map() and starmap() parallels the distinction between - function(a,b) and function(*c). + function(a,b) and function(\*c). :param function: the function to apply :param iterable: where groups of arguments come from From d07a3496a02602f645219a2b493a6f460c45b3f5 Mon Sep 17 00:00:00 2001 From: Dave Astels Date: Sat, 23 Mar 2019 10:52:29 -0400 Subject: [PATCH 12/15] Remove todos and add example --- README.rst | 27 ++++++++++++++++++++++++--- 1 file changed, 24 insertions(+), 3 deletions(-) diff --git a/README.rst b/README.rst index cbf010c..eba2133 100644 --- a/README.rst +++ b/README.rst @@ -30,8 +30,7 @@ Installing from PyPI -------------------- .. note:: This library is not available on PyPI yet. Install documentation is included as a standard element. Stay tuned for PyPI availability! -.. todo:: Remove the above note if PyPI version is/will be available at time of release. - If the library is not planned for PyPI, remove the entire 'Installing from PyPI' section. + On supported GNU/Linux systems like the Raspberry Pi, you can install the driver locally `from PyPI `_. To install for current user: @@ -57,7 +56,29 @@ To install in a virtual environment in your current project: Usage Example ============= -.. todo:: Add a quick, simple example. It and other examples should live in the examples folder and be included in docs/examples.rst. +.. code-block:: python + + import time + import board + import busio + import adafruit_si7021 + from adafruit_itertools.adafruit_itertools import count + from adafruit_itertools.adafruit_itertools_extras import repeatfunc + + i2c = busio.I2C(board.SCL, board.SDA) + sensor = adafruit_si7021.SI7021(i2c) + + def read_temperature(): + return sensor.temperature + + def now(): + return time.monotonic() + + datapoints = zip(count(1), repeatfunc(now), map(int, repeatfunc(read_temperature))) + + while True: + print(next(datapoints)) + time.sleep(20.0) Contributing ============ From 2d237ed1f3b343b0c093642f30cbcde07aac0567 Mon Sep 17 00:00:00 2001 From: Dave Astels Date: Sat, 23 Mar 2019 11:00:04 -0400 Subject: [PATCH 13/15] Shut pylint up about something needed to shut sphinx up. --- adafruit_itertools/adafruit_itertools.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/adafruit_itertools/adafruit_itertools.py b/adafruit_itertools/adafruit_itertools.py index ac53d7a..7d6441d 100644 --- a/adafruit_itertools/adafruit_itertools.py +++ b/adafruit_itertools/adafruit_itertools.py @@ -39,7 +39,7 @@ https://github.com/adafruit/circuitpython/releases """ #pylint:disable=invalid-name,redefined-builtin,attribute-defined-outside-init -#pylint:disable=stop-iteration-return +#pylint:disable=stop-iteration-return,anomalous-backslash-in-string __version__ = "0.0.0-auto.0" __repo__ = "https://github.com/adafruit/Adafruit_CircuitPython_Itertools.git" From e2e9b86ecaf513d9a26351d11e2a7e28b5dd6656 Mon Sep 17 00:00:00 2001 From: Dave Astels Date: Tue, 26 Mar 2019 13:39:09 -0400 Subject: [PATCH 14/15] Swap in PSF licensing --- LICENSE | 54 ++++++++++----- adafruit_itertools/adafruit_itertools.py | 67 +++++++++++------- .../adafruit_itertools_extras.py | 69 ++++++++++++------- 3 files changed, 126 insertions(+), 64 deletions(-) diff --git a/LICENSE b/LICENSE index d69195f..2f35565 100644 --- a/LICENSE +++ b/LICENSE @@ -1,21 +1,41 @@ -The MIT License (MIT) +1. This LICENSE AGREEMENT is between the Python Software Foundation ("PSF"), and + the Individual or Organization ("Licensee") accessing and otherwise using Python + 3.7.3 software in source or binary form and its associated documentation. -Copyright (c) 2019 Dave Astels for Adafruit Industries +2. Subject to the terms and conditions of this License Agreement, PSF hereby + grants Licensee a nonexclusive, royalty-free, world-wide license to reproduce, + analyze, test, perform and/or display publicly, prepare derivative works, + distribute, and otherwise use Python 3.7.3 alone or in any derivative + version, provided, however, that PSF's License Agreement and PSF's notice of + copyright, i.e., "Copyright © 2001-2019 Python Software Foundation; All Rights + Reserved" are retained in Python 3.7.3 alone or in any derivative version + prepared by Licensee. -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: +3. In the event Licensee prepares a derivative work that is based on or + incorporates Python 3.7.3 or any part thereof, and wants to make the + derivative work available to others as provided herein, then Licensee hereby + agrees to include in any such work a brief summary of the changes made to Python + 3.7.3. -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. +4. PSF is making Python 3.7.3 available to Licensee on an "AS IS" basis. + PSF MAKES NO REPRESENTATIONS OR WARRANTIES, EXPRESS OR IMPLIED. BY WAY OF + EXAMPLE, BUT NOT LIMITATION, PSF MAKES NO AND DISCLAIMS ANY REPRESENTATION OR + WARRANTY OF MERCHANTABILITY OR FITNESS FOR ANY PARTICULAR PURPOSE OR THAT THE + USE OF PYTHON 3.7.3 WILL NOT INFRINGE ANY THIRD PARTY RIGHTS. -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. +5. PSF SHALL NOT BE LIABLE TO LICENSEE OR ANY OTHER USERS OF PYTHON 3.7.3 + FOR ANY INCIDENTAL, SPECIAL, OR CONSEQUENTIAL DAMAGES OR LOSS AS A RESULT OF + MODIFYING, DISTRIBUTING, OR OTHERWISE USING PYTHON 3.7.3, OR ANY DERIVATIVE + THEREOF, EVEN IF ADVISED OF THE POSSIBILITY THEREOF. + +6. This License Agreement will automatically terminate upon a material breach of + its terms and conditions. + +7. Nothing in this License Agreement shall be deemed to create any relationship + of agency, partnership, or joint venture between PSF and Licensee. This License + Agreement does not grant permission to use PSF trademarks or trade name in a + trademark sense to endorse or promote products or services of Licensee, or any + third party. + +8. By copying, installing or otherwise using Python 3.7.3, Licensee agrees + to be bound by the terms and conditions of this License Agreement. diff --git a/adafruit_itertools/adafruit_itertools.py b/adafruit_itertools/adafruit_itertools.py index 7d6441d..601fd62 100644 --- a/adafruit_itertools/adafruit_itertools.py +++ b/adafruit_itertools/adafruit_itertools.py @@ -1,32 +1,53 @@ -# The MIT License (MIT) -# -# Copyright (c) 2019 Dave Astels for Adafruit Industries -# -# Permission is hereby granted, free of charge, to any person obtaining a copy -# of this software and associated documentation files (the "Software"), to deal -# in the Software without restriction, including without limitation the rights -# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -# copies of the Software, and to permit persons to whom the Software is -# furnished to do so, subject to the following conditions: -# -# The above copyright notice and this permission notice shall be included in -# all copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -# THE SOFTWARE. +# 1. This LICENSE AGREEMENT is between the Python Software Foundation ("PSF"), and +# the Individual or Organization ("Licensee") accessing and otherwise using Python +# 3.7.3 software in source or binary form and its associated documentation. + +# 2. Subject to the terms and conditions of this License Agreement, PSF hereby +# grants Licensee a nonexclusive, royalty-free, world-wide license to reproduce, +# analyze, test, perform and/or display publicly, prepare derivative works, +# distribute, and otherwise use Python 3.7.3 alone or in any derivative +# version, provided, however, that PSF's License Agreement and PSF's notice of +# copyright, i.e., "Copyright © 2001-2019 Python Software Foundation; All Rights +# Reserved" are retained in Python 3.7.3 alone or in any derivative version +# prepared by Licensee. + +# 3. In the event Licensee prepares a derivative work that is based on or +# incorporates Python 3.7.3 or any part thereof, and wants to make the +# derivative work available to others as provided herein, then Licensee hereby +# agrees to include in any such work a brief summary of the changes made to Python +# 3.7.3. + +# 4. PSF is making Python 3.7.3 available to Licensee on an "AS IS" basis. +# PSF MAKES NO REPRESENTATIONS OR WARRANTIES, EXPRESS OR IMPLIED. BY WAY OF +# EXAMPLE, BUT NOT LIMITATION, PSF MAKES NO AND DISCLAIMS ANY REPRESENTATION OR +# WARRANTY OF MERCHANTABILITY OR FITNESS FOR ANY PARTICULAR PURPOSE OR THAT THE +# USE OF PYTHON 3.7.3 WILL NOT INFRINGE ANY THIRD PARTY RIGHTS. + +# 5. PSF SHALL NOT BE LIABLE TO LICENSEE OR ANY OTHER USERS OF PYTHON 3.7.3 +# FOR ANY INCIDENTAL, SPECIAL, OR CONSEQUENTIAL DAMAGES OR LOSS AS A RESULT OF +# MODIFYING, DISTRIBUTING, OR OTHERWISE USING PYTHON 3.7.3, OR ANY DERIVATIVE +# THEREOF, EVEN IF ADVISED OF THE POSSIBILITY THEREOF. + +# 6. This License Agreement will automatically terminate upon a material breach of +# its terms and conditions. + +# 7. Nothing in this License Agreement shall be deemed to create any relationship +# of agency, partnership, or joint venture between PSF and Licensee. This License +# Agreement does not grant permission to use PSF trademarks or trade name in a +# trademark sense to endorse or promote products or services of Licensee, or any +# third party. + +# 8. By copying, installing or otherwise using Python 3.7.3, Licensee agrees +# to be bound by the terms and conditions of this License Agreement. """ `adafruit_itertools` ================================================================================ -Python's itertools for CircuitPython +Python's itertools adapted for CircuitPython by Dave Astels +Copyright 2001-2019 Python Software Foundation; All Rights Reserved -* Author(s): Dave Astels +* Author(s): The PSF and Dave Astels Implementation Notes -------------------- diff --git a/adafruit_itertools/adafruit_itertools_extras.py b/adafruit_itertools/adafruit_itertools_extras.py index d846122..87fb2da 100644 --- a/adafruit_itertools/adafruit_itertools_extras.py +++ b/adafruit_itertools/adafruit_itertools_extras.py @@ -1,34 +1,54 @@ -# The MIT License (MIT) -# -# Copyright (c) 2019 Dave Astels for Adafruit Industries -# -# Permission is hereby granted, free of charge, to any person obtaining a copy -# of this software and associated documentation files (the "Software"), to deal -# in the Software without restriction, including without limitation the rights -# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -# copies of the Software, and to permit persons to whom the Software is -# furnished to do so, subject to the following conditions: -# -# The above copyright notice and this permission notice shall be included in -# all copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -# THE SOFTWARE. +# 1. This LICENSE AGREEMENT is between the Python Software Foundation ("PSF"), and +# the Individual or Organization ("Licensee") accessing and otherwise using Python +# 3.7.3 software in source or binary form and its associated documentation. + +# 2. Subject to the terms and conditions of this License Agreement, PSF hereby +# grants Licensee a nonexclusive, royalty-free, world-wide license to reproduce, +# analyze, test, perform and/or display publicly, prepare derivative works, +# distribute, and otherwise use Python 3.7.3 alone or in any derivative +# version, provided, however, that PSF's License Agreement and PSF's notice of +# copyright, i.e., "Copyright © 2001-2019 Python Software Foundation; All Rights +# Reserved" are retained in Python 3.7.3 alone or in any derivative version +# prepared by Licensee. + +# 3. In the event Licensee prepares a derivative work that is based on or +# incorporates Python 3.7.3 or any part thereof, and wants to make the +# derivative work available to others as provided herein, then Licensee hereby +# agrees to include in any such work a brief summary of the changes made to Python +# 3.7.3. + +# 4. PSF is making Python 3.7.3 available to Licensee on an "AS IS" basis. +# PSF MAKES NO REPRESENTATIONS OR WARRANTIES, EXPRESS OR IMPLIED. BY WAY OF +# EXAMPLE, BUT NOT LIMITATION, PSF MAKES NO AND DISCLAIMS ANY REPRESENTATION OR +# WARRANTY OF MERCHANTABILITY OR FITNESS FOR ANY PARTICULAR PURPOSE OR THAT THE +# USE OF PYTHON 3.7.3 WILL NOT INFRINGE ANY THIRD PARTY RIGHTS. + +# 5. PSF SHALL NOT BE LIABLE TO LICENSEE OR ANY OTHER USERS OF PYTHON 3.7.3 +# FOR ANY INCIDENTAL, SPECIAL, OR CONSEQUENTIAL DAMAGES OR LOSS AS A RESULT OF +# MODIFYING, DISTRIBUTING, OR OTHERWISE USING PYTHON 3.7.3, OR ANY DERIVATIVE +# THEREOF, EVEN IF ADVISED OF THE POSSIBILITY THEREOF. + +# 6. This License Agreement will automatically terminate upon a material breach of +# its terms and conditions. + +# 7. Nothing in this License Agreement shall be deemed to create any relationship +# of agency, partnership, or joint venture between PSF and Licensee. This License +# Agreement does not grant permission to use PSF trademarks or trade name in a +# trademark sense to endorse or promote products or services of Licensee, or any +# third party. + +# 8. By copying, installing or otherwise using Python 3.7.3, Licensee agrees +# to be bound by the terms and conditions of this License Agreement. """ `adafruit_itertools_extras` ================================================================================ -Extras for itertools for CircuitPython +Extras for Python itertools adapted for CircuitPython by Dave Astels This module contains an extended toolset using the existing itertools as building blocks. -The extended tools offer the same high performance as the underlying +The extended tools offer the same performance as the underlying toolset. The superior memory performance is kept by processing elements one at a time rather than bringing the whole iterable into memory all at once. Code volume is kept small by linking the tools together in a functional style which @@ -36,8 +56,9 @@ "vectorized" building blocks over the use of for-loops and generators which incur interpreter overhead. +Copyright 2001-2019 Python Software Foundation; All Rights Reserved -* Author(s): Dave Astels +* Author(s): The PSF and Dave Astels Implementation Notes -------------------- From 9620080273d4fb3580ad661370bd7758665ff068 Mon Sep 17 00:00:00 2001 From: Dave Astels Date: Tue, 26 Mar 2019 19:22:47 -0400 Subject: [PATCH 15/15] Remove PyPI related things --- README.rst | 30 ++--------------------- requirements.txt | 1 - setup.py | 63 ------------------------------------------------ 3 files changed, 2 insertions(+), 92 deletions(-) delete mode 100644 setup.py diff --git a/README.rst b/README.rst index eba2133..6215043 100644 --- a/README.rst +++ b/README.rst @@ -9,8 +9,8 @@ Introduction :target: https://discord.gg/nBQh6qu :alt: Discord -.. image:: https://travis-ci.com/adafruit/Adafruit_CircuitPython_itertools.svg?branch=master - :target: https://travis-ci.com/adafruit/Adafruit_CircuitPython_itertools +.. image:: https://travis-ci.com/adafruit/Adafruit_CircuitPython_IterTools.svg?branch=master + :target: https://travis-ci.com/adafruit/Adafruit_CircuitPython_IterTools :alt: Build Status Python's itertools for CircuitPython @@ -26,32 +26,6 @@ Please ensure all dependencies are available on the CircuitPython filesystem. This is easily achieved by downloading `the Adafruit library and driver bundle `_. -Installing from PyPI --------------------- -.. note:: This library is not available on PyPI yet. Install documentation is included - as a standard element. Stay tuned for PyPI availability! - -On supported GNU/Linux systems like the Raspberry Pi, you can install the driver locally `from -PyPI `_. To install for current user: - -.. code-block:: shell - - pip3 install adafruit-circuitpython-itertools - -To install system-wide (this may be required in some cases): - -.. code-block:: shell - - sudo pip3 install adafruit-circuitpython-itertools - -To install in a virtual environment in your current project: - -.. code-block:: shell - - mkdir project-name && cd project-name - python3 -m venv .env - source .env/bin/activate - pip3 install adafruit-circuitpython-itertools Usage Example ============= diff --git a/requirements.txt b/requirements.txt index edf9394..e69de29 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1 +0,0 @@ -Adafruit-Blinka diff --git a/setup.py b/setup.py deleted file mode 100644 index 0e4c51a..0000000 --- a/setup.py +++ /dev/null @@ -1,63 +0,0 @@ -"""A setuptools based setup module. - -See: -https://packaging.python.org/en/latest/distributing.html -https://github.com/pypa/sampleproject -""" - -from setuptools import setup, find_packages -# To use a consistent encoding -from codecs import open -from os import path - -here = path.abspath(path.dirname(__file__)) - -# Get the long description from the README file -with open(path.join(here, 'README.rst'), encoding='utf-8') as f: - long_description = f.read() - -setup( - name='adafruit-circuitpython-itertools', - - use_scm_version=True, - setup_requires=['setuptools_scm'], - - description='Python's itertools for CircuitPython', - long_description=long_description, - long_description_content_type='text/x-rst', - - # The project's main homepage. - url='https://github.com/adafruit/Adafruit_CircuitPython_Itertools', - - # Author details - author='Adafruit Industries', - author_email='circuitpython@adafruit.com', - - install_requires=[ - 'Adafruit-Blinka' - ], - - # Choose your license - license='MIT', - - # See https://pypi.python.org/pypi?%3Aaction=list_classifiers - classifiers=[ - 'Development Status :: 3 - Alpha', - 'Intended Audience :: Developers', - 'Topic :: Software Development :: Libraries', - 'Topic :: System :: Hardware', - 'License :: OSI Approved :: MIT License', - 'Programming Language :: Python :: 3', - 'Programming Language :: Python :: 3.4', - 'Programming Language :: Python :: 3.5', - ], - - # What does your project relate to? - keywords='adafruit blinka circuitpython micropython itertools programming tools', - - # You can just specify the packages manually here if your project is - # simple. Or you can use find_packages(). - # TODO: IF LIBRARY FILES ARE A PACKAGE FOLDER, - # CHANGE `py_modules=['...']` TO `packages=['...']` - py_modules=['adafruit_itertools', 'adafruit_itertools_extras'], -)