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..1d09583 --- /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/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/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..2f35565 --- /dev/null +++ b/LICENSE @@ -0,0 +1,41 @@ +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. 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..6215043 --- /dev/null +++ b/README.rst @@ -0,0 +1,112 @@ +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 `_. + + +Usage Example +============= + +.. 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 +============ + +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/adafruit_itertools.py b/adafruit_itertools/adafruit_itertools.py new file mode 100644 index 0000000..601fd62 --- /dev/null +++ b/adafruit_itertools/adafruit_itertools.py @@ -0,0 +1,533 @@ +# 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 adapted for CircuitPython by Dave Astels + +Copyright 2001-2019 Python Software Foundation; All Rights Reserved + +* Author(s): The PSF and 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,redefined-builtin,attribute-defined-outside-init +#pylint:disable=stop-iteration-return,anomalous-backslash-in-string + +__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: + index = 0 + for i in reversed(range(r)): + if indices[i] != i + n - r: + index = i + break + else: + return + indices[index] += 1 + for j in range(index+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: + index = 0 + for i in reversed(range(r)): + if indices[i] != n - 1: + index = i + break + else: + return + indices[index:] = [indices[index] + 1] * (r - index) + 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. + + :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 _ in range(start): + next(it) + + while True: + yield next(it) + for _ 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, r=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 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 + # product(range(2), repeat=3) --> 000 001 010 011 100 101 110 111 + pools = [tuple(pool) for pool in args] * r + 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 _ 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/adafruit_itertools_extras.py b/adafruit_itertools/adafruit_itertools_extras.py new file mode 100644 index 0000000..87fb2da --- /dev/null +++ b/adafruit_itertools/adafruit_itertools_extras.py @@ -0,0 +1,341 @@ +# 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 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 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. + +Copyright 2001-2019 Python Software Foundation; All Rights Reserved + +* Author(s): The PSF and 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 0000000..5aca983 Binary files /dev/null and b/docs/_static/favicon.ico differ diff --git a/docs/api.rst b/docs/api.rst new file mode 100644 index 0000000..890013e --- /dev/null +++ b/docs/api.rst @@ -0,0 +1,11 @@ + +.. If you created a package, create one automodule per module in the package. + +.. 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.adafruit_itertools + :members: + +.. automodule:: adafruit_itertools.adafruit_itertools_extras + :members: diff --git a/docs/conf.py b/docs/conf.py new file mode 100644 index 0000000..582c6c9 --- /dev/null +++ b/docs/conf.py @@ -0,0 +1,153 @@ +# -*- coding: utf-8 -*- + +import os +import sys +sys.path.insert(0, os.path.abspath('..')) + +# -- General configuration ------------------------------------------------ + +# Add any Sphinx extension module names here, as strings. They can be +# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom +# ones. +extensions = [ + 'sphinx.ext.autodoc', + 'sphinx.ext.intersphinx', + 'sphinx.ext.napoleon', + 'sphinx.ext.todo', +] + +intersphinx_mapping = {'python': ('https://docs.python.org/3.4', None),'CircuitPython': ('https://circuitpython.readthedocs.io/en/latest/', None)} + +# Add any paths that contain templates here, relative to this directory. +templates_path = ['_templates'] + +source_suffix = '.rst' + +# The master toctree document. +master_doc = 'index' + +# General information about the project. +project = u'Adafruit itertools Library' +copyright = u'2019 Dave Astels' +author = u'Dave Astels' + +# The version info for the project you're documenting, acts as replacement for +# |version| and |release|, also used in various other places throughout the +# built documents. +# +# The short X.Y version. +version = u'1.0' +# The full version, including alpha/beta/rc tags. +release = u'1.0' + +# The language for content autogenerated by Sphinx. Refer to documentation +# for a list of supported languages. +# +# This is also used if you do content translation via gettext catalogs. +# Usually you set "language" from the command line for these cases. +language = None + +# List of patterns, relative to source directory, that match files and +# directories to ignore when looking for source files. +# This patterns also effect to html_static_path and html_extra_path +exclude_patterns = ['_build', 'Thumbs.db', '.DS_Store', '.env', 'CODE_OF_CONDUCT.md'] + +# The reST default role (used for this markup: `text`) to use for all +# documents. +# +default_role = "any" + +# If true, '()' will be appended to :func: etc. cross-reference text. +# +add_function_parentheses = True + +# The name of the Pygments (syntax highlighting) style to use. +pygments_style = 'sphinx' + +# If true, `todo` and `todoList` produce output, else they produce nothing. +todo_include_todos = False + +# If this is True, todo emits a warning for each TODO entries. The default is False. +todo_emit_warnings = True + +napoleon_numpy_docstring = False + +# -- Options for HTML output ---------------------------------------------- + +# The theme to use for HTML and HTML Help pages. See the documentation for +# a list of builtin themes. +# +on_rtd = os.environ.get('READTHEDOCS', None) == 'True' + +if not on_rtd: # only import and set the theme if we're building docs locally + try: + import sphinx_rtd_theme + html_theme = 'sphinx_rtd_theme' + html_theme_path = [sphinx_rtd_theme.get_html_theme_path(), '.'] + except: + html_theme = 'default' + html_theme_path = ['.'] +else: + html_theme_path = ['.'] + +# Add any paths that contain custom static files (such as style sheets) here, +# relative to this directory. They are copied after the builtin static files, +# so a file named "default.css" will overwrite the builtin "default.css". +html_static_path = ['_static'] + +# The name of an image file (relative to this directory) to use as a favicon of +# the docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32 +# pixels large. +# +html_favicon = '_static/favicon.ico' + +# Output file base name for HTML help builder. +htmlhelp_basename = 'AdafruitItertoolsLibrarydoc' + +# -- Options for LaTeX output --------------------------------------------- + +latex_elements = { + # The paper size ('letterpaper' or 'a4paper'). + # + # 'papersize': 'letterpaper', + + # The font size ('10pt', '11pt' or '12pt'). + # + # 'pointsize': '10pt', + + # Additional stuff for the LaTeX preamble. + # + # 'preamble': '', + + # Latex figure (float) alignment + # + # 'figure_align': 'htbp', +} + +# Grouping the document tree into LaTeX files. List of tuples +# (source start file, target name, title, +# author, documentclass [howto, manual, or own class]). +latex_documents = [ + (master_doc, 'AdafruititertoolsLibrary.tex', u'Adafruititertools Library Documentation', + author, 'manual'), +] + +# -- Options for manual page output --------------------------------------- + +# One entry per manual page. List of tuples +# (source start file, name, description, authors, manual section). +man_pages = [ + (master_doc, 'Adafruititertoolslibrary', u'Adafruit itertools Library Documentation', + [author], 1) +] + +# -- Options for Texinfo output ------------------------------------------- + +# Grouping the document tree into Texinfo files. List of tuples +# (source start file, target name, title, author, +# dir menu entry, description, category) +texinfo_documents = [ + (master_doc, 'AdafruititertoolsLibrary', u'Adafruit itertools Library Documentation', + author, 'AdafruititertoolsLibrary', 'One line description of project.', + 'Miscellaneous'), +] diff --git a/docs/examples.rst b/docs/examples.rst new file mode 100644 index 0000000..1af4e30 --- /dev/null +++ b/docs/examples.rst @@ -0,0 +1,8 @@ +Simple test +------------ + +Ensure your device works with this simple test. + +.. literalinclude:: ../examples/itertools_simpletest.py + :caption: examples/itertools_simpletest.py + :linenos: diff --git a/docs/index.rst b/docs/index.rst new file mode 100644 index 0000000..109c750 --- /dev/null +++ b/docs/index.rst @@ -0,0 +1,47 @@ +.. include:: ../README.rst + +Table of Contents +================= + +.. toctree:: + :maxdepth: 4 + :hidden: + + self + +.. toctree:: + :caption: Examples + + examples + +.. toctree:: + :caption: API Reference + :maxdepth: 3 + + api + +.. toctree:: + :caption: Tutorials + + CircuitPython 101: Working with Lists, Iterators and Generators + +.. 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..d2a5b77 --- /dev/null +++ b/examples/itertools_simpletest.py @@ -0,0 +1,47 @@ +# 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.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) diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..e69de29