├── .editorconfig ├── .github └── ISSUE_TEMPLATE.md ├── .gitignore ├── .pylintrc ├── .travis.yml ├── AUTHORS.rst ├── CONTRIBUTING.rst ├── HISTORY.rst ├── LICENSE ├── MANIFEST.in ├── Makefile ├── README.rst ├── circuitpython_kernel ├── __init__.py ├── __main__.py ├── board.py ├── install.py ├── kernel.py └── version.py ├── docs ├── Makefile ├── _static │ └── jupyterlab_blinky.gif ├── authors.rst ├── boardprep.md ├── circuitpython_kernel.rst ├── conf.py ├── contributing.rst ├── environment.yml ├── history.rst ├── index.rst ├── installation.rst ├── make.bat ├── modules.rst ├── readme.rst └── usage.rst ├── examples ├── CPX_Blink_Pico.ipynb ├── CPX_Blink_Teensy.ipynb ├── CPX_Blinka.ipynb ├── CPX_NeoPixels.ipynb ├── Using_magics.ipynb └── using_magics_and_nbdev.ipynb ├── readthedocs.yml ├── requirements_dev.txt ├── setup.cfg ├── setup.py ├── tests ├── __init__.py └── test_circuitpython_kernel.py └── travis_pypi_setup.py /.editorconfig: -------------------------------------------------------------------------------- 1 | # http://editorconfig.org 2 | 3 | root = true 4 | 5 | [*] 6 | indent_style = space 7 | indent_size = 4 8 | trim_trailing_whitespace = true 9 | insert_final_newline = true 10 | charset = utf-8 11 | end_of_line = lf 12 | 13 | [*.bat] 14 | indent_style = tab 15 | end_of_line = crlf 16 | 17 | [LICENSE] 18 | insert_final_newline = false 19 | 20 | [Makefile] 21 | indent_style = tab 22 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | * CircuitPython Kernel version: 2 | * Python version: 3 | * Operating System: 4 | 5 | ### Description 6 | 7 | Describe what you were trying to get done. 8 | Tell us what happened, what went wrong, and what you expected to happen. 9 | 10 | ### What I Did 11 | 12 | ``` 13 | Paste the command(s) you ran and the output. 14 | If there was a crash, please include the traceback here. 15 | ``` 16 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Byte-compiled / optimized / DLL files 2 | __pycache__/ 3 | *.py[cod] 4 | *$py.class 5 | 6 | # C extensions 7 | *.so 8 | 9 | # Distribution / packaging 10 | .Python 11 | env/ 12 | build/ 13 | develop-eggs/ 14 | dist/ 15 | downloads/ 16 | eggs/ 17 | .eggs/ 18 | lib/ 19 | lib64/ 20 | parts/ 21 | sdist/ 22 | var/ 23 | *.egg-info/ 24 | .installed.cfg 25 | *.egg 26 | 27 | # PyInstaller 28 | # Usually these files are written by a python script from a template 29 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 30 | *.manifest 31 | *.spec 32 | 33 | # Installer logs 34 | pip-log.txt 35 | pip-delete-this-directory.txt 36 | 37 | # Unit test / coverage reports 38 | htmlcov/ 39 | .tox/ 40 | .coverage 41 | .coverage.* 42 | .cache 43 | nosetests.xml 44 | coverage.xml 45 | *,cover 46 | .hypothesis/ 47 | 48 | # Translations 49 | *.mo 50 | *.pot 51 | 52 | # Django stuff: 53 | *.log 54 | 55 | # Sphinx documentation 56 | docs/_build/ 57 | 58 | # PyBuilder 59 | target/ 60 | 61 | # pyenv python configuration file 62 | .python-version 63 | 64 | # Jupyter 65 | .ipynb_checkpoints 66 | -------------------------------------------------------------------------------- /.pylintrc: -------------------------------------------------------------------------------- 1 | [MASTER] 2 | 3 | # Specify a configuration file. 4 | #rcfile= 5 | 6 | # Python code to execute, usually for sys.path manipulation such as 7 | # pygtk.require(). 8 | #init-hook= 9 | 10 | # Profiled execution. 11 | profile=no 12 | 13 | # Add files or directories to the blacklist. They should be base names, not 14 | # paths. 15 | ignore=CVS,setup,install 16 | 17 | # Pickle collected data for later comparisons. 18 | persistent=yes 19 | 20 | # List of plugins (as comma separated values of python modules names) to load, 21 | # usually to register additional checkers. 22 | load-plugins= 23 | 24 | # DEPRECATED 25 | include-ids=no 26 | 27 | # DEPRECATED 28 | symbols=no 29 | 30 | 31 | # Use multiple processes to speed up Pylint. 32 | jobs=2 33 | 34 | [MESSAGES CONTROL] 35 | 36 | # Enable the message, report, category or checker with the given id(s). You can 37 | # either give multiple identifier separated by comma (,) or put this option 38 | # multiple time. See also the "--disable" option for examples. 39 | #enable= 40 | 41 | # Disable the message, report, category or checker with the given id(s). You 42 | # can either give multiple identifiers separated by comma (,) or put this 43 | # option multiple times (only on the command line, not in the configuration 44 | # file where it should appear only once).You can also use "--disable=all" to 45 | # disable everything first and then reenable specific checks. For example, if 46 | # you want to run only the similarities checker, you can use "--disable=all 47 | # --enable=similarities". If you want to run only the classes checker, but have 48 | # no Warning level messages displayed, use"--disable=all --enable=classes 49 | # --disable=W" 50 | disable=abstract-method,too-many-arguments,inconsistent-return-statements, bad-continuation 51 | 52 | 53 | [REPORTS] 54 | 55 | # Set the output format. Available formats are text, parseable, colorized, msvs 56 | # (visual studio) and html. You can also give a reporter class, eg 57 | # mypackage.mymodule.MyReporterClass. 58 | output-format=text 59 | 60 | # Put messages in a separate file for each module / package specified on the 61 | # command line instead of printing them on stdout. Reports (if any) will be 62 | # written in a file name "pylint_global.[txt|html]". 63 | files-output=no 64 | 65 | # Tells whether to display a full report or only the messages 66 | reports=yes 67 | 68 | # Python expression which should return a note less than 10 (10 is the highest 69 | # note). You have access to the variables errors warning, statement which 70 | # respectively contain the number of errors / warnings messages and the total 71 | # number of statements analyzed. This is used by the global evaluation report 72 | # (RP0004). 73 | evaluation=10.0 - ((float(5 * error + warning + refactor + convention) / statement) * 10) 74 | 75 | # Add a comment according to your evaluation note. This is used by the global 76 | # evaluation report (RP0004). 77 | comment=no 78 | 79 | # Template used to display messages. This is a python new-style format string 80 | # used to format the message information. See doc for all details 81 | #msg-template= 82 | 83 | 84 | [BASIC] 85 | 86 | # Required attributes for module, separated by a comma 87 | required-attributes= 88 | 89 | # List of builtins function names that should not be used, separated by a comma 90 | bad-functions=map,filter,apply,input,file 91 | 92 | # Good variable names which should always be accepted, separated by a comma 93 | good-names=i,j,k,ex,Run,_ 94 | 95 | # Bad variable names which should always be refused, separated by a comma 96 | bad-names=foo,bar,baz,toto,tutu,tata 97 | 98 | # Colon-delimited sets of names that determine each other's naming style when 99 | # the name regexes allow several styles. 100 | name-group= 101 | 102 | # Include a hint for the correct naming format with invalid-name 103 | include-naming-hint=no 104 | 105 | # Regular expression matching correct function names 106 | function-rgx=[a-z_][a-z0-9_]{2,30}$ 107 | 108 | # Naming hint for function names 109 | function-name-hint=[a-z_][a-z0-9_]{2,30}$ 110 | 111 | # Regular expression matching correct variable names 112 | variable-rgx=[a-z_][a-z0-9_]{2,30}$ 113 | 114 | # Naming hint for variable names 115 | variable-name-hint=[a-z_][a-z0-9_]{2,30}$ 116 | 117 | # Regular expression matching correct constant names 118 | const-rgx=(([A-Z_][A-Z0-9_]*)|(__.*__))$ 119 | 120 | # Naming hint for constant names 121 | const-name-hint=(([A-Z_][A-Z0-9_]*)|(__.*__))$ 122 | 123 | # Regular expression matching correct attribute names 124 | attr-rgx=[a-z_][a-z0-9_]{2,30}$ 125 | 126 | # Naming hint for attribute names 127 | attr-name-hint=[a-z_][a-z0-9_]{2,30}$ 128 | 129 | # Regular expression matching correct argument names 130 | argument-rgx=[a-z_][a-z0-9_]{2,30}$ 131 | 132 | # Naming hint for argument names 133 | argument-name-hint=[a-z_][a-z0-9_]{2,30}$ 134 | 135 | # Regular expression matching correct class attribute names 136 | class-attribute-rgx=([A-Za-z_][A-Za-z0-9_]{2,30}|(__.*__))$ 137 | 138 | # Naming hint for class attribute names 139 | class-attribute-name-hint=([A-Za-z_][A-Za-z0-9_]{2,30}|(__.*__))$ 140 | 141 | # Regular expression matching correct inline iteration names 142 | inlinevar-rgx=[A-Za-z_][A-Za-z0-9_]*$ 143 | 144 | # Naming hint for inline iteration names 145 | inlinevar-name-hint=[A-Za-z_][A-Za-z0-9_]*$ 146 | 147 | # Regular expression matching correct class names 148 | class-rgx=[A-Z_][a-zA-Z0-9]+$ 149 | 150 | # Naming hint for class names 151 | class-name-hint=[A-Z_][a-zA-Z0-9]+$ 152 | 153 | # Regular expression matching correct module names 154 | module-rgx=(([a-z_][a-z0-9_]*)|([A-Z][a-zA-Z0-9]+))$ 155 | 156 | # Naming hint for module names 157 | module-name-hint=(([a-z_][a-z0-9_]*)|([A-Z][a-zA-Z0-9]+))$ 158 | 159 | # Regular expression matching correct method names 160 | method-rgx=[a-z_][a-z0-9_]{2,30}$ 161 | 162 | # Naming hint for method names 163 | method-name-hint=[a-z_][a-z0-9_]{2,30}$ 164 | 165 | # Regular expression which should only match function or class names that do 166 | # not require a docstring. 167 | no-docstring-rgx=__.*__ 168 | 169 | # Minimum line length for functions/classes that require docstrings, shorter 170 | # ones are exempt. 171 | docstring-min-length=-1 172 | 173 | 174 | [FORMAT] 175 | 176 | # Maximum number of characters on a single line. 177 | max-line-length=120 178 | 179 | # Regexp for a line that is allowed to be longer than the limit. 180 | ignore-long-lines=^\s*(# )??$ 181 | 182 | # Allow the body of an if to be on the same line as the test if there is no 183 | # else. 184 | single-line-if-stmt=no 185 | 186 | # List of optional constructs for which whitespace checking is disabled 187 | no-space-check=trailing-comma,dict-separator 188 | 189 | # Maximum number of lines in a module 190 | max-module-lines=1000 191 | 192 | # String used as indentation unit. This is usually " " (4 spaces) or "\t" (1 193 | # tab). 194 | indent-string=' ' 195 | 196 | # Number of spaces of indent required inside a hanging or continued line. 197 | indent-after-paren=4 198 | 199 | 200 | [LOGGING] 201 | 202 | # Logging modules to check that the string format arguments are in logging 203 | # function parameter format 204 | logging-modules=logging 205 | 206 | 207 | [MISCELLANEOUS] 208 | 209 | # List of note tags to take in consideration, separated by a comma. 210 | notes=FIXME,XXX,TODO 211 | 212 | 213 | [SIMILARITIES] 214 | 215 | # Minimum lines number of a similarity. 216 | min-similarity-lines=4 217 | 218 | # Ignore comments when computing similarities. 219 | ignore-comments=yes 220 | 221 | # Ignore docstrings when computing similarities. 222 | ignore-docstrings=yes 223 | 224 | # Ignore imports when computing similarities. 225 | ignore-imports=no 226 | 227 | 228 | [TYPECHECK] 229 | 230 | # Tells whether missing members accessed in mixin class should be ignored. A 231 | # mixin class is detected if its name ends with "mixin" (case insensitive). 232 | ignore-mixin-members=yes 233 | 234 | # List of module names for which member attributes should not be checked 235 | # (useful for modules/projects where namespaces are manipulated during runtime 236 | # and thus existing member attributes cannot be deduced by static analysis 237 | ignored-modules= 238 | 239 | # List of classes names for which member attributes should not be checked 240 | # (useful for classes with attributes dynamically set). 241 | ignored-classes=SQLObject 242 | 243 | # When zope mode is activated, add a predefined set of Zope acquired attributes 244 | # to generated-members. 245 | zope=no 246 | 247 | # List of members which are set dynamically and missed by pylint inference 248 | # system, and so shouldn't trigger E0201 when accessed. Python regular 249 | # expressions are accepted. 250 | generated-members=REQUEST,acl_users,aq_parent 251 | 252 | 253 | [VARIABLES] 254 | 255 | # Tells whether we should check for unused import in __init__ files. 256 | init-import=no 257 | 258 | # A regular expression matching the name of dummy variables (i.e. expectedly 259 | # not used). 260 | dummy-variables-rgx=_$|dummy 261 | 262 | # List of additional names supposed to be defined in builtins. Remember that 263 | # you should avoid to define new builtins when possible. 264 | additional-builtins= 265 | 266 | 267 | [CLASSES] 268 | 269 | # List of interface methods to ignore, separated by a comma. This is used for 270 | # instance to not check methods defines in Zope's Interface base class. 271 | ignore-iface-methods=isImplementedBy,deferred,extends,names,namesAndDescriptions,queryDescriptionFor,getBases,getDescriptionFor,getDoc,getName,getTaggedValue,getTaggedValueTags,isEqualOrExtendedBy,setTaggedValue,isImplementedByInstancesOf,adaptWith,is_implemented_by 272 | 273 | # List of method names used to declare (i.e. assign) instance attributes. 274 | defining-attr-methods=__init__,__new__,setUp 275 | 276 | # List of valid names for the first argument in a class method. 277 | valid-classmethod-first-arg=cls 278 | 279 | # List of valid names for the first argument in a metaclass class method. 280 | valid-metaclass-classmethod-first-arg=mcs 281 | 282 | 283 | [DESIGN] 284 | 285 | # Maximum number of arguments for function / method 286 | max-args=5 287 | 288 | # Argument names that match this expression will be ignored. Default to name 289 | # with leading underscore 290 | ignored-argument-names=_.* 291 | 292 | # Maximum number of locals for function / method body 293 | max-locals=15 294 | 295 | # Maximum number of return / yield for function / method body 296 | max-returns=6 297 | 298 | # Maximum number of branch for function / method body 299 | max-branches=12 300 | 301 | # Maximum number of statements in function / method body 302 | max-statements=50 303 | 304 | # Maximum number of parents for a class (see R0901). 305 | max-parents=7 306 | 307 | # Maximum number of attributes for a class (see R0902). 308 | max-attributes=7 309 | 310 | # Minimum number of public methods for a class (see R0903). 311 | min-public-methods=2 312 | 313 | # Maximum number of public methods for a class (see R0904). 314 | max-public-methods=20 315 | 316 | 317 | [IMPORTS] 318 | 319 | # Deprecated modules which should not be used, separated by a comma 320 | deprecated-modules=regsub,TERMIOS,Bastion,rexec 321 | 322 | # Create a graph of every (i.e. internal and external) dependencies in the 323 | # given file (report RP0402 must not be disabled) 324 | import-graph= 325 | 326 | # Create a graph of external dependencies in the given file (report RP0402 must 327 | # not be disabled) 328 | ext-import-graph= 329 | 330 | # Create a graph of internal dependencies in the given file (report RP0402 must 331 | # not be disabled) 332 | int-import-graph= 333 | 334 | 335 | [EXCEPTIONS] 336 | 337 | # Exceptions that will emit a warning when being caught. Defaults to 338 | # "Exception" 339 | overgeneral-exceptions=Exception 340 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | # Config file for automatic testing at travis-ci.org 2 | # This file will be regenerated if you run travis_pypi_setup.py 3 | 4 | language: python 5 | python: 3.6 6 | 7 | install: 8 | - pip install -r requirements_dev.txt 9 | - pip install -e . 10 | - python setup.py install 11 | 12 | script: 13 | - python setup.py test 14 | - pylint circuitpython_kernel/board.py circuitpython_kernel/kernel.py 15 | -------------------------------------------------------------------------------- /AUTHORS.rst: -------------------------------------------------------------------------------- 1 | ======= 2 | Credits 3 | ======= 4 | 5 | Development Lead 6 | ---------------- 7 | 8 | * Carol Willing 9 | 10 | Contributors 11 | ------------ 12 | 13 | * Brent Rubell 14 | -------------------------------------------------------------------------------- /CONTRIBUTING.rst: -------------------------------------------------------------------------------- 1 | .. highlight:: shell 2 | 3 | ============ 4 | Contributing 5 | ============ 6 | 7 | Contributions are welcome, and they are greatly appreciated! Every 8 | little bit helps, and credit will always be given. 9 | 10 | You can contribute in many ways: 11 | 12 | Types of Contributions 13 | ---------------------- 14 | 15 | Report Bugs 16 | ~~~~~~~~~~~ 17 | 18 | Report bugs at https://github.com/adafruit/circuitpython_kernel/issues. 19 | 20 | If you are reporting a bug, please include: 21 | 22 | * Your operating system name and version. 23 | * Any details about your local setup that might be helpful in troubleshooting. 24 | * Detailed steps to reproduce the bug. 25 | 26 | Fix Bugs 27 | ~~~~~~~~ 28 | 29 | Look through the GitHub issues for bugs. Anything tagged with "bug" 30 | and "help wanted" is open to whoever wants to implement it. 31 | 32 | Implement Features 33 | ~~~~~~~~~~~~~~~~~~ 34 | 35 | Look through the GitHub issues for features. Anything tagged with "enhancement" 36 | and "help wanted" is open to whoever wants to implement it. 37 | 38 | Write Documentation 39 | ~~~~~~~~~~~~~~~~~~~ 40 | 41 | CircuitPython Kernel could always use more documentation, whether as part of the 42 | official CircuitPython Kernel docs, in docstrings, or even on the web in blog posts, 43 | articles, and such. 44 | 45 | Submit Feedback 46 | ~~~~~~~~~~~~~~~ 47 | 48 | The best way to send feedback is to file an issue at https://github.com/adafruit/circuitpython_kernel/issues. 49 | 50 | If you are proposing a feature: 51 | 52 | * Explain in detail how it would work. 53 | * Keep the scope as narrow as possible, to make it easier to implement. 54 | * Remember that this is a volunteer-driven project, and that contributions 55 | are welcome :) 56 | 57 | Get Started! 58 | ------------ 59 | 60 | Ready to contribute? Here's how to set up `circuitpython_kernel` for local development. 61 | 62 | 1. Fork the `circuitpython_kernel` repo on GitHub. 63 | 2. Clone your fork locally:: 64 | 65 | $ git clone git@github.com:your_name_here/circuitpython_kernel.git 66 | 67 | 3. Install your local copy into a virtualenv. Assuming you have virtualenvwrapper installed, this is how you set up your fork for local development:: 68 | 69 | $ mkvirtualenv circuitpython_kernel 70 | $ cd circuitpython_kernel/ 71 | $ python setup.py develop 72 | 73 | 4. Create a branch for local development:: 74 | 75 | $ git checkout -b name-of-your-bugfix-or-feature 76 | 77 | Now you can make your changes locally. 78 | 79 | 5. When you're done making changes, check that your changes pass flake8 and the tests, including testing other Python versions with tox:: 80 | 81 | $ flake8 circuitpython_kernel tests 82 | $ python setup.py test or py.test 83 | $ tox 84 | 85 | To get flake8 and tox, just pip install them into your virtualenv. 86 | 87 | 6. Commit your changes and push your branch to GitHub:: 88 | 89 | $ git add . 90 | $ git commit -m "Your detailed description of your changes." 91 | $ git push origin name-of-your-bugfix-or-feature 92 | 93 | 7. Submit a pull request through the GitHub website. 94 | 95 | Pull Request Guidelines 96 | ----------------------- 97 | 98 | Before you submit a pull request, check that it meets these guidelines: 99 | 100 | 1. The pull request should include tests. 101 | 2. If the pull request adds functionality, the docs should be updated. Put 102 | your new functionality into a function with a docstring, and add the 103 | feature to the list in README.rst. 104 | 3. The pull request should work for Python 3.6 and higher. Check 105 | https://travis-ci.org/adafruit/circuitpython_kernel/pull_requests 106 | and make sure that the tests pass for all supported Python versions. 107 | 108 | Tips 109 | ---- 110 | 111 | To run a subset of tests:: 112 | 113 | 114 | $ python -m unittest tests.test_circuitpython_kernel 115 | -------------------------------------------------------------------------------- /HISTORY.rst: -------------------------------------------------------------------------------- 1 | ======= 2 | History 3 | ======= 4 | 5 | 0.3.2 (2018-11-27) 6 | ------------------ 7 | 8 | 0.3.1 (2018-07-04) 9 | ------------------ 10 | 11 | 0.3.0 (2018-06-20) 12 | ------------------ 13 | 14 | 0.2.0 (2018-02-08) 15 | ------------------ 16 | 17 | * update examples 18 | 19 | 20 | 0.1.0 (2017-03-23) 21 | ------------------ 22 | 23 | * First release on PyPI. 24 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | 2 | BSD License 3 | 4 | Copyright (c) 2017, Carol Willing 5 | All rights reserved. 6 | 7 | Redistribution and use in source and binary forms, with or without modification, 8 | are permitted provided that the following conditions are met: 9 | 10 | * Redistributions of source code must retain the above copyright notice, this 11 | list of conditions and the following disclaimer. 12 | 13 | * Redistributions in binary form must reproduce the above copyright notice, this 14 | list of conditions and the following disclaimer in the documentation and/or 15 | other materials provided with the distribution. 16 | 17 | * Neither the name of the copyright holder nor the names of its 18 | contributors may be used to endorse or promote products derived from this 19 | software without specific prior written permission. 20 | 21 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 22 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 23 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 24 | IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, 25 | INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 26 | BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 27 | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY 28 | OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE 29 | OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED 30 | OF THE POSSIBILITY OF SUCH DAMAGE. 31 | 32 | -------------------------------------------------------------------------------- /MANIFEST.in: -------------------------------------------------------------------------------- 1 | 2 | include AUTHORS.rst 3 | 4 | include CONTRIBUTING.rst 5 | include HISTORY.rst 6 | include LICENSE 7 | include README.rst 8 | 9 | recursive-include tests * 10 | recursive-exclude * __pycache__ 11 | recursive-exclude * *.py[co] 12 | 13 | recursive-include docs *.rst conf.py Makefile make.bat *.jpg *.png *.gif 14 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | .PHONY: clean clean-test clean-pyc clean-build docs help 2 | .DEFAULT_GOAL := help 3 | define BROWSER_PYSCRIPT 4 | import os, webbrowser, sys 5 | try: 6 | from urllib import pathname2url 7 | except: 8 | from urllib.request import pathname2url 9 | 10 | webbrowser.open("file://" + pathname2url(os.path.abspath(sys.argv[1]))) 11 | endef 12 | export BROWSER_PYSCRIPT 13 | 14 | define PRINT_HELP_PYSCRIPT 15 | import re, sys 16 | 17 | for line in sys.stdin: 18 | match = re.match(r'^([a-zA-Z_-]+):.*?## (.*)$$', line) 19 | if match: 20 | target, help = match.groups() 21 | print("%-20s %s" % (target, help)) 22 | endef 23 | export PRINT_HELP_PYSCRIPT 24 | BROWSER := python -c "$$BROWSER_PYSCRIPT" 25 | 26 | help: 27 | @python -c "$$PRINT_HELP_PYSCRIPT" < $(MAKEFILE_LIST) 28 | 29 | clean: clean-build clean-pyc clean-test ## remove all build, test, coverage and Python artifacts 30 | 31 | 32 | clean-build: ## remove build artifacts 33 | rm -fr build/ 34 | rm -fr dist/ 35 | rm -fr .eggs/ 36 | find . -name '*.egg-info' -exec rm -fr {} + 37 | find . -name '*.egg' -exec rm -f {} + 38 | 39 | clean-pyc: ## remove Python file artifacts 40 | find . -name '*.pyc' -exec rm -f {} + 41 | find . -name '*.pyo' -exec rm -f {} + 42 | find . -name '*~' -exec rm -f {} + 43 | find . -name '__pycache__' -exec rm -fr {} + 44 | 45 | clean-test: ## remove test and coverage artifacts 46 | rm -fr .tox/ 47 | rm -f .coverage 48 | rm -fr htmlcov/ 49 | 50 | lint: ## check style with flake8 51 | flake8 circuitpython_kernel tests 52 | 53 | test: ## run tests quickly with the default Python 54 | 55 | python setup.py test 56 | 57 | coverage: ## check code coverage quickly with the default Python 58 | 59 | coverage run --source circuitpython_kernel setup.py test 60 | 61 | coverage report -m 62 | coverage html 63 | $(BROWSER) htmlcov/index.html 64 | 65 | docs: ## generate Sphinx HTML documentation, including API docs 66 | rm -f docs/circuitpython_kernel.rst 67 | rm -f docs/modules.rst 68 | sphinx-apidoc -o docs/ circuitpython_kernel 69 | $(MAKE) -C docs clean 70 | $(MAKE) -C docs html 71 | $(BROWSER) docs/_build/html/index.html 72 | 73 | servedocs: docs ## compile the docs watching for changes 74 | watchmedo shell-command -p '*.rst' -c '$(MAKE) -C docs html' -R -D . 75 | 76 | release: clean ## package and upload a release 77 | python setup.py sdist upload 78 | python setup.py bdist_wheel upload 79 | 80 | dist: clean ## builds source and wheel package 81 | python setup.py sdist 82 | python setup.py bdist_wheel 83 | ls -l dist 84 | 85 | install: clean ## install the package to the active Python's site-packages 86 | python setup.py install 87 | -------------------------------------------------------------------------------- /README.rst: -------------------------------------------------------------------------------- 1 | CircuitPython Kernel 2 | ==================== 3 | 4 | .. image:: https://cdn-learn.adafruit.com/guides/images/000/002/051/medium310/Untitled-3.png?1528919538 5 | 6 | 7 | .. image:: https://readthedocs.org/projects/circuitpython-kernel/badge/?version=latest 8 | :target: https://circuitpython-kernel.readthedocs.io/en/latest/?badge=latest 9 | :alt: Documentation 10 | 11 | 12 | .. image:: https://img.shields.io/discord/327254708534116352.svg 13 | :target: https://adafru.it/discord 14 | :alt: Discord 15 | 16 | 17 | .. image:: https://img.shields.io/travis/adafruit/circuitpython_kernel.svg 18 | :target: https://travis-ci.org/adafruit/circuitpython_kernel 19 | :alt: Build Status 20 | 21 | 22 | The CircuitPython Kernel is a `Jupyter Kernel `_ designed to interact with Adafruit boards 23 | running `CircuitPython `_ from within a Jupyter Notebook. 24 | 25 | 26 | Status 27 | ------ 28 | 29 | This project's status is experimental. It has been tested with CircuitPython 3.x in (SAMD) boards and 30 | Feather HUZZAH (ESP8266). 31 | With CircuitPython 6.x in Raspberry Pi Pico 32 | 33 | It may break, and if it does, please file an 34 | `issue on this repository `__. 35 | 36 | 37 | Compatible Boards 38 | ----------------- 39 | 40 | Designed for CircuitPython (SAMD21 and SAMD51) 41 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 42 | 43 | - `Adafruit CircuitPlayground Express `__ 44 | - `Adafruit Feather M0 Express `__ 45 | - `Adafruit Metro M0 Express `_ 46 | - `Adafruit Metro M4 Express `_ 47 | - `Adafruit Trinket M0 `__ 48 | - `Adafruit Gemma M0 `__ 49 | - `Adafruit Trinket M0 `__ 50 | - `Adafruit ItsyBitsy M0 Express `_ 51 | - `Adafruit ItsyBitsy M4 Express `__ 52 | 53 | 54 | 55 | Other Adafruit Boards 56 | ~~~~~~~~~~~~~~~~~~~~~ 57 | 58 | - `Adafruit Feather HUZZAH ESP8266 `__ 59 | 60 | Other Boards 61 | ~~~~~~~~~~~~~~~~~~~~~ 62 | - `Raspberry Pi Pico `__ 63 | - `PJRC Teensy 4.1 __ 64 | 65 | Download 66 | -------- 67 | 68 | Official .zip files are available through the 69 | `latest GitHub releases `__. 70 | 71 | 72 | Install 73 | ------- 74 | 75 | Jupyter:: 76 | 77 | pip3 install --upgrade pip 78 | pip3 install jupyter 79 | 80 | Optional:: 81 | 82 | pip3 install jupyterlab 83 | 84 | CircuitPython kernel:: 85 | 86 | cd circuitpython_kernel/ 87 | python3 setup.py install; python3 -m circuitpython_kernel.install 88 | 89 | Then run with one of:: 90 | 91 | jupyter notebook 92 | jupyter lab 93 | 94 | and choose the CircuitPython kernel. 95 | 96 | Documentation 97 | ------------- 98 | 99 | This kernel is fully documented on the Adafruit Learning System Guide: 100 | `CircuitPython with Jupyter Notebooks `__. 101 | 102 | A line containing exactly the word:: 103 | 104 | %softreset 105 | 106 | will reset the board and release all resources. 107 | 108 | There's also documentation for this kernel listed on 109 | `ReadTheDocs `__. 110 | -------------------------------------------------------------------------------- /circuitpython_kernel/__init__.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """A Jupyter kernel for CircuitPython""" 3 | 4 | from .version import version_info, __version__ 5 | 6 | __author__ = """Adafruit Industries""" 7 | __email__ = 'brent@adafruit.com' 8 | -------------------------------------------------------------------------------- /circuitpython_kernel/__main__.py: -------------------------------------------------------------------------------- 1 | """Kernel Launcher""" 2 | import logging 3 | 4 | from ipykernel.kernelapp import IPKernelApp 5 | from .kernel import CircuitPyKernel 6 | 7 | logging.basicConfig(level=logging.DEBUG) 8 | 9 | IPKernelApp.launch_instance(kernel_class=CircuitPyKernel) 10 | -------------------------------------------------------------------------------- /circuitpython_kernel/board.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """Serial Connection to a Board""" 3 | import logging 4 | from serial import Serial 5 | from serial.tools.list_ports import comports 6 | from serial.serialutil import SerialException 7 | 8 | # Create BOARD_LOGGER for debug messages. 9 | BOARD_LOGGER = logging.getLogger(__name__) 10 | 11 | 12 | # Vendor IDs 13 | ADAFRUIT_VID = 0x239A # SAMD 14 | ESP8266_VID = 0x10C4 # Huzzah ESP8266 15 | PICO_VID = 0x239A # PICO PI 16 | TEENSY_VID = 0x16C0 #PJRC Teensy 4.1 17 | 18 | # repl commands 19 | CHAR_CTRL_A = b'\x01' 20 | CHAR_CTRL_B = b'\x02' 21 | CHAR_CTRL_C = b'\x03' 22 | CHAR_CTRL_D = b'\x04' 23 | 24 | # repl messages 25 | MSG_NEWLINE = b"\r\n" 26 | MSG_RAWREPL = b"raw REPL; CTRL-B to exit" 27 | MSG_RAWREPL_PROMPT = b"\r\n>" 28 | MSG_SOFT_REBOOT = b'soft reboot\r\n' 29 | MSG_RELOAD = b'Use CTRL-D to reload.' 30 | 31 | class BoardError(Exception): 32 | """Errors relating to board connections""" 33 | def __init__(self, msg): 34 | # pylint: disable=useless-super-delegation 35 | super().__init__(msg) 36 | 37 | 38 | class Board: 39 | """Connect to CP VM, reconnect if connection lost""" 40 | 41 | def __init__(self): 42 | self.connected = False 43 | self.serial = None 44 | 45 | def write(self, msg): 46 | """Writes to CircuitPython Board. 47 | """ 48 | try: 49 | self.serial.write(msg) 50 | except SerialException as serial_error: 51 | self.connected = False 52 | raise BoardError(f"cannot write to board: {serial_error}") 53 | 54 | 55 | def read_until(self, msg): 56 | """Reads board until end of `msg`. 57 | """ 58 | try: 59 | return self.serial.read_until(msg) 60 | except SerialException as serial_error: 61 | self.connected = False 62 | raise BoardError(f"cannot read from board: {serial_error}") 63 | 64 | 65 | def read_all(self): 66 | """Attempts to read all incoming msgs from board. 67 | """ 68 | try: 69 | return self.serial.read_all() 70 | except SerialException as serial_error: 71 | self.connected = False 72 | raise BoardError(f"cannot read from board: {serial_error}") 73 | 74 | def close(self): 75 | """Close serial connection with board. 76 | """ 77 | if self.serial and self.connected: 78 | try: 79 | self.connected = False 80 | self.serial.close() 81 | except SerialException: 82 | pass 83 | 84 | def softreset(self): 85 | """Resets the circuitpython board (^D) 86 | from a jupyter cell. 87 | """ 88 | serial = self.serial 89 | # in case the VM is in a weird state ... 90 | self.enter_raw_repl() 91 | # now do the soft reset ... 92 | BOARD_LOGGER.debug("* ^D, soft reset") 93 | serial.write(CHAR_CTRL_D) 94 | serial.read_until(MSG_SOFT_REBOOT) 95 | serial.read_until(MSG_RELOAD) 96 | serial.write(b'\n') 97 | serial.read_until(MSG_RAWREPL) 98 | serial.read_until(MSG_RAWREPL_PROMPT) 99 | BOARD_LOGGER.debug("* soft reset complete, in raw repl") 100 | 101 | def enter_raw_repl(self): 102 | """Enters the RAW circuitpython repl. 103 | """ 104 | BOARD_LOGGER.debug('* enter raw repl ...') 105 | serial = self.serial 106 | serial.write(CHAR_CTRL_C) 107 | serial.write(CHAR_CTRL_A) 108 | # wait for prompt 109 | serial.read_until(MSG_RAWREPL) 110 | serial.read_until(MSG_RAWREPL_PROMPT) 111 | BOARD_LOGGER.debug('* entered raw repl, returning to kernel...') 112 | 113 | def connect(self): 114 | """(re)connect to board and enter raw repl 115 | """ 116 | if self.connected: 117 | return 118 | # pylint : disable=too-many-function-args 119 | device = self._find_board() 120 | try: 121 | BOARD_LOGGER.debug(f'connect: open {device}') 122 | self.serial = Serial(device, 115200, parity='N') 123 | except: 124 | raise BoardError(f"failed to access {device}") 125 | # open the port 126 | if not self.serial.is_open: 127 | try: 128 | BOARD_LOGGER.debug('* opening board ...') 129 | self.serial.open() 130 | BOARD_LOGGER.debug('* board opened') 131 | except SerialException as serial_error: 132 | raise BoardError(f"failed to open {device}, {serial_error}") 133 | else: 134 | BOARD_LOGGER.debug('serial already open') 135 | 136 | # enter the REPL 137 | try: 138 | self.enter_raw_repl() 139 | self.connected = True 140 | except: 141 | raise BoardError(f"failed to enter raw repl with {device}") 142 | 143 | 144 | def _find_board(self): 145 | """Find serial port where an Adafruit board is connected""" 146 | for port in comports(): 147 | # print out each device 148 | BOARD_LOGGER.debug(port.device) 149 | if port.vid == ADAFRUIT_VID or port.vid == ESP8266_VID or port.vid == PICO_VID or port.vid == TEENSY_VID: 150 | BOARD_LOGGER.debug(f"CircuitPython Board Found at: {port.device}") 151 | BOARD_LOGGER.debug(f"Connected? {self.connected}") 152 | return port.device 153 | raise BoardError("found no board") 154 | -------------------------------------------------------------------------------- /circuitpython_kernel/install.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """Kernelspec installation.""" 3 | 4 | import getopt 5 | import json 6 | import os 7 | import sys 8 | 9 | from IPython.utils.tempdir import TemporaryDirectory 10 | from jupyter_client.kernelspec import KernelSpecManager 11 | 12 | kernel_json = { 13 | "argv": ["python", "-m", "circuitpython_kernel", "-f", "{connection_file}"], 14 | "display_name": "CircuitPython", 15 | "mimetype": "text/x-python", 16 | "language": "python", 17 | "name": "circuitpython", 18 | } 19 | 20 | 21 | def install_my_kernel_spec(user=True, prefix=None): 22 | """Install circuitpython kernel to list of kernels.""" 23 | with TemporaryDirectory() as temp_dir: 24 | os.chmod(temp_dir, 0o755) # Starts off as 700, not user readable 25 | with open(os.path.join(temp_dir, 'kernel.json'), 'w') as f: 26 | json.dump(kernel_json, f, sort_keys=True) 27 | print('Installing CircuitPython kernelspec') 28 | KernelSpecManager().install_kernel_spec( 29 | temp_dir, 'circuitpython', user=user, replace=True, prefix=prefix 30 | ) 31 | print('Completed kernel installation.') 32 | 33 | 34 | def _is_root(): 35 | try: 36 | return os.geteuid() == 0 37 | 38 | except AttributeError: 39 | return False # assume not an admin on non-Unix platforms 40 | 41 | 42 | def main(argv=None): 43 | if argv is None: 44 | argv = [] 45 | prefix = None 46 | user = not _is_root() 47 | 48 | opts, _ = getopt.getopt(argv[1:], '', ['user', 'prefix=']) 49 | for k, v in opts: 50 | if k == '--user': 51 | user = True 52 | elif k == '--prefix': 53 | prefix = v 54 | user = False 55 | 56 | install_my_kernel_spec(user=user, prefix=prefix) 57 | 58 | 59 | if __name__ == '__main__': 60 | main(argv=sys.argv) 61 | -------------------------------------------------------------------------------- /circuitpython_kernel/kernel.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """Basic functionality of CircuitPython kernel.""" 3 | import ast 4 | import logging 5 | import re 6 | import time 7 | 8 | from ipykernel.kernelbase import Kernel 9 | from serial.serialutil import SerialException 10 | from .board import Board, BoardError 11 | from .version import __version__ 12 | 13 | # Create global KERNEL_LOGGER for debug messages. 14 | KERNEL_LOGGER = logging.getLogger(__name__) 15 | 16 | 17 | class CircuitPyKernel(Kernel): 18 | """CircuitPython kernel implementation.""" 19 | 20 | protocol_version = '4.5.2' 21 | implementation = 'circuitpython_kernel' 22 | implementation_version = __version__ 23 | language_info = { 24 | 'name': 'python', 25 | 'version': '3', 26 | 'mimetype': 'text/x-python', 27 | 'file_extension': '.py', 28 | 'pygments_lexer': 'python3', 29 | 'codemirror_mode': {'name': 'python', 'version': 3}, 30 | } 31 | banner = "CircuitPython" 32 | help_links = [ 33 | { 34 | 'text': 'CircuitPython kernel', 35 | 'url': 'https://github.com/adafruit/circuitpython_kernel', 36 | } 37 | ] 38 | 39 | def __init__(self, **kwargs): 40 | """Set up connection to board""" 41 | super().__init__(**kwargs) 42 | KERNEL_LOGGER.debug(f"circuitpython_kernel version {__version__}") 43 | self.board = Board() 44 | self.upload_delay = 0.06 45 | 46 | def is_magic(self, line): 47 | """Returns true if line was handled""" 48 | if line.startswith("%softreset"): 49 | self.board.softreset() 50 | elif line.startswith("%upload_delay"): 51 | try: 52 | s_line = line.split(' ') 53 | self.upload_delay = float(s_line[1]) 54 | KERNEL_LOGGER.debug(f"upload_delay set to {float(s_line[1])} s") 55 | except TypeError: 56 | pass 57 | elif line.startswith("%python"): 58 | #python line magic, runs what ever is on the line following the %python magic. 59 | code = line.lstrip("%python") 60 | code = code.lstrip(' ') 61 | for item in code.split(";"): 62 | item = item.lstrip(' ') #remove leading white space 63 | try: 64 | print(eval(item)) #does not print 65 | except: 66 | out = exec(item) 67 | if out != None: 68 | print(out) #does not print 69 | 70 | else: 71 | return False 72 | return True 73 | 74 | def is_cell_magic(self, code): 75 | """Cell magic to run python code. 76 | ----- 77 | Cell shall begin with %%python followed by a new line 78 | Will iteratively run each line of code. 79 | """ 80 | 81 | if code.startswith("%%python"): 82 | code = code.lstrip("%%python") 83 | code = code.lstrip(' ') 84 | data = code.splitlines(True) 85 | for item in data: 86 | 87 | code = code.lstrip(' ') #this removes all preceeding white space, 88 | #i need to figure out how to get for loops, etc working 89 | try: 90 | print(eval(item)) #does not print 91 | except: 92 | out = exec(item) 93 | 94 | if out != None: 95 | print(out) #does not print 96 | return True 97 | else: 98 | return False 99 | 100 | @classmethod 101 | def is_comment(cls, line): 102 | """Returns true if the line of code is empty or a comment. 103 | 104 | It is much faster to check and skip these lines on the host then 105 | to send them to the MCU (with an `upload_delay` between each one). 106 | """ 107 | line = line.strip() 108 | return len(line) == 0 or line.startswith("#") 109 | 110 | def run_code(self, code): 111 | """Run a code snippet. 112 | 113 | Parameters 114 | ---------- 115 | code : str 116 | Code to be executed. 117 | 118 | Returns 119 | ------- 120 | out 121 | Decoded bytearray output result from code run. 122 | err 123 | Decoded bytearray error from code run. 124 | 125 | """ 126 | # make sure we are connected to the board 127 | self.board.connect() 128 | ##cell check for python cell magics 129 | python_cell = self.is_cell_magic(code) 130 | 131 | if python_cell == True: 132 | out = [] 133 | err = [] 134 | return out, err 135 | # Send code to board & fetch results (if any) after each line sent 136 | for line in code.splitlines(False): 137 | if not self.is_magic(line) and not self.is_comment(line): 138 | self.board.write(line.encode('utf-8')) 139 | self.board.write(b'\r\n') 140 | # The Featherboard M4 cannot keep up with long code cells 141 | time.sleep(self.upload_delay) 142 | # Kick off evaluation ... 143 | self.board.write(b'\r\x04') # Control-D 144 | # Set up a bytearray to hold the result from the code run 145 | result = bytearray() 146 | while not result.endswith(b'\x04>'): # Control-D 147 | time.sleep(0.1) 148 | result.extend(self.board.read_all()) 149 | KERNEL_LOGGER.debug('received: %s', result.decode('utf-8', 'replace')) 150 | 151 | assert result.startswith(b'OK') 152 | out, err = result[2:-2].split(b'\x04', 1) # split result 153 | 154 | return out.decode('utf-8', 'replace'), err.decode('utf-8', 'replace') 155 | 156 | def do_execute(self, code, silent, store_history=True, 157 | user_expressions=None, allow_stdin=False): 158 | """Execute a user's code cell. 159 | 160 | Parameters 161 | ---------- 162 | code : str 163 | Code, one or more lines, to be executed. 164 | silent : bool 165 | True, signals kernel to execute code quietly, and output is not 166 | displayed. 167 | store_history : bool 168 | Whether to record code in history and increase execution count. If 169 | silent is True, this is implicitly false. 170 | user_expressions : dict, optional 171 | Mapping of names to expressions to evaluate after code is run. 172 | allow_stdin : bool 173 | Whether the frontend can provide input on request (e.g. for 174 | Python’s raw_input()). 175 | 176 | Returns 177 | ------- 178 | dict 179 | Execution results. 180 | 181 | """ 182 | if not code.strip(): 183 | return {'status': 'ok', 184 | 'execution_count': self.execution_count, 185 | 'payload': [], 186 | 'user_expressions': {}} 187 | # evaluate code on board 188 | out = err = None 189 | try: 190 | out, err = self.run_code(code) 191 | except (BoardError, SerialException) as ser_eror: 192 | KERNEL_LOGGER.debug(f'no connection {ser_eror}') 193 | err = f"No connection to CiruitPython VM: {ser_eror}" 194 | except KeyboardInterrupt: 195 | KERNEL_LOGGER.debug(f'keyboard interrupt') 196 | err = "Keyboard Interrupt" 197 | if out: 198 | KERNEL_LOGGER.debug(f"Output: '{out}'") 199 | if err: 200 | KERNEL_LOGGER.debug(f"Error: '{err}'") 201 | if not silent: 202 | out_content = {'name': 'stdout', 'text': out} 203 | err_content = {'name': 'stderr', 'text': err} 204 | if out: 205 | self.send_response(self.iopub_socket, 'stream', out_content) 206 | if err: 207 | self.send_response(self.iopub_socket, 'stream', err_content) 208 | 209 | return { 210 | 'status': 'ok', 211 | 'execution_count': self.execution_count, 212 | 'payload': [], 213 | 'user_expressions': {}, 214 | } 215 | 216 | def _eval(self, expr): 217 | """Evaluate the expression. 218 | 219 | Use ast's literal_eval to prevent strange input from execution. 220 | 221 | """ 222 | try: 223 | out, err = self.run_code('print({})'.format(expr)) 224 | except (BoardError, SerialException) as ser_eror: 225 | out = err = f"Lost connection to CiruitPython VM: {ser_eror}" 226 | KERNEL_LOGGER.debug('Output: %s', out) 227 | KERNEL_LOGGER.debug('Error %s', err) 228 | return ast.literal_eval(out) 229 | 230 | def do_shutdown(self, restart): 231 | """Handle the kernel shutting down.""" 232 | KERNEL_LOGGER.debug('Shutting down CircuitPython Board Connection..') 233 | 234 | # If we try to disconnect before sending any commands, the `write()` 235 | # call here will fail. So, make sure we are connected to the board. 236 | self.board.connect() 237 | self.board.write(b'\r\x02') 238 | 239 | KERNEL_LOGGER.debug('closing board connection..') 240 | self.board.close() 241 | 242 | def do_complete(self, code, cursor_pos): 243 | """Support code completion.""" 244 | code = code[:cursor_pos] 245 | match = re.search(r'(\w+\.)*(\w+)?$', code) 246 | if match: 247 | prefix = match.group() 248 | if '.' in prefix: 249 | obj, prefix = prefix.rsplit('.') 250 | names = self._eval('dir({})'.format(obj)) 251 | else: 252 | names = self._eval('dir()') 253 | matches = [n for n in names if n.startswith(prefix)] 254 | return { 255 | 'matches': matches, 256 | 'cursor_start': cursor_pos - len(prefix), 257 | 'cursor_end': cursor_pos, 258 | 'metadata': {}, 259 | 'status': 'ok', 260 | } 261 | 262 | else: 263 | return { 264 | 'matches': [], 265 | 'cursor_start': cursor_pos, 266 | 'cursor_end': cursor_pos, 267 | 'metadata': {}, 268 | 'status': 'ok', 269 | } 270 | -------------------------------------------------------------------------------- /circuitpython_kernel/version.py: -------------------------------------------------------------------------------- 1 | """CircuitPython Kernel version info""" 2 | 3 | # Copyright (c) Carol Willing. 4 | # Distributed under the terms of the Modified BSD License. 5 | 6 | version_info = (0, 3, 0, 'dev') 7 | 8 | __version__ = '.'.join(map(str, version_info)) 9 | -------------------------------------------------------------------------------- /docs/Makefile: -------------------------------------------------------------------------------- 1 | # Makefile for Sphinx documentation 2 | # 3 | 4 | # You can set these variables from the command line. 5 | SPHINXOPTS = 6 | SPHINXBUILD = sphinx-build 7 | PAPER = 8 | BUILDDIR = _build 9 | 10 | # User-friendly check for sphinx-build 11 | ifeq ($(shell which $(SPHINXBUILD) >/dev/null 2>&1; echo $$?), 1) 12 | $(error The '$(SPHINXBUILD)' command was not found. Make sure you have Sphinx installed, then set the SPHINXBUILD environment variable to point to the full path of the '$(SPHINXBUILD)' executable. Alternatively you can add the directory with the executable to your PATH. If you don't have Sphinx installed, grab it from http://sphinx-doc.org/) 13 | endif 14 | 15 | # Internal variables. 16 | PAPEROPT_a4 = -D latex_paper_size=a4 17 | PAPEROPT_letter = -D latex_paper_size=letter 18 | ALLSPHINXOPTS = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) . 19 | # the i18n builder cannot share the environment and doctrees with the others 20 | I18NSPHINXOPTS = $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) . 21 | 22 | .PHONY: help clean html dirhtml singlehtml pickle json htmlhelp qthelp devhelp epub latex latexpdf text man changes linkcheck doctest gettext 23 | 24 | help: 25 | @echo "Please use \`make ' where is one of" 26 | @echo " html to make standalone HTML files" 27 | @echo " dirhtml to make HTML files named index.html in directories" 28 | @echo " singlehtml to make a single large HTML file" 29 | @echo " pickle to make pickle files" 30 | @echo " json to make JSON files" 31 | @echo " htmlhelp to make HTML files and a HTML help project" 32 | @echo " qthelp to make HTML files and a qthelp project" 33 | @echo " devhelp to make HTML files and a Devhelp project" 34 | @echo " epub to make an epub" 35 | @echo " latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter" 36 | @echo " latexpdf to make LaTeX files and run them through pdflatex" 37 | @echo " latexpdfja to make LaTeX files and run them through platex/dvipdfmx" 38 | @echo " text to make text files" 39 | @echo " man to make manual pages" 40 | @echo " texinfo to make Texinfo files" 41 | @echo " info to make Texinfo files and run them through makeinfo" 42 | @echo " gettext to make PO message catalogs" 43 | @echo " changes to make an overview of all changed/added/deprecated items" 44 | @echo " xml to make Docutils-native XML files" 45 | @echo " pseudoxml to make pseudoxml-XML files for display purposes" 46 | @echo " linkcheck to check all external links for integrity" 47 | @echo " doctest to run all doctests embedded in the documentation (if enabled)" 48 | 49 | clean: 50 | rm -rf $(BUILDDIR)/* 51 | 52 | html: 53 | $(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html 54 | @echo 55 | @echo "Build finished. The HTML pages are in $(BUILDDIR)/html." 56 | 57 | dirhtml: 58 | $(SPHINXBUILD) -b dirhtml $(ALLSPHINXOPTS) $(BUILDDIR)/dirhtml 59 | @echo 60 | @echo "Build finished. The HTML pages are in $(BUILDDIR)/dirhtml." 61 | 62 | singlehtml: 63 | $(SPHINXBUILD) -b singlehtml $(ALLSPHINXOPTS) $(BUILDDIR)/singlehtml 64 | @echo 65 | @echo "Build finished. The HTML page is in $(BUILDDIR)/singlehtml." 66 | 67 | pickle: 68 | $(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) $(BUILDDIR)/pickle 69 | @echo 70 | @echo "Build finished; now you can process the pickle files." 71 | 72 | json: 73 | $(SPHINXBUILD) -b json $(ALLSPHINXOPTS) $(BUILDDIR)/json 74 | @echo 75 | @echo "Build finished; now you can process the JSON files." 76 | 77 | htmlhelp: 78 | $(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) $(BUILDDIR)/htmlhelp 79 | @echo 80 | @echo "Build finished; now you can run HTML Help Workshop with the" \ 81 | ".hhp project file in $(BUILDDIR)/htmlhelp." 82 | 83 | qthelp: 84 | $(SPHINXBUILD) -b qthelp $(ALLSPHINXOPTS) $(BUILDDIR)/qthelp 85 | @echo 86 | @echo "Build finished; now you can run "qcollectiongenerator" with the" \ 87 | ".qhcp project file in $(BUILDDIR)/qthelp, like this:" 88 | @echo "# qcollectiongenerator $(BUILDDIR)/qthelp/circuitpython_kernel.qhcp" 89 | @echo "To view the help file:" 90 | @echo "# assistant -collectionFile $(BUILDDIR)/qthelp/circuitpython_kernel.qhc" 91 | 92 | devhelp: 93 | $(SPHINXBUILD) -b devhelp $(ALLSPHINXOPTS) $(BUILDDIR)/devhelp 94 | @echo 95 | @echo "Build finished." 96 | @echo "To view the help file:" 97 | @echo "# mkdir -p $$HOME/.local/share/devhelp/circuitpython_kernel" 98 | @echo "# ln -s $(BUILDDIR)/devhelp $$HOME/.local/share/devhelp/circuitpython_kernel" 99 | @echo "# devhelp" 100 | 101 | epub: 102 | $(SPHINXBUILD) -b epub $(ALLSPHINXOPTS) $(BUILDDIR)/epub 103 | @echo 104 | @echo "Build finished. The epub file is in $(BUILDDIR)/epub." 105 | 106 | latex: 107 | $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex 108 | @echo 109 | @echo "Build finished; the LaTeX files are in $(BUILDDIR)/latex." 110 | @echo "Run \`make' in that directory to run these through (pdf)latex" \ 111 | "(use \`make latexpdf' here to do that automatically)." 112 | 113 | latexpdf: 114 | $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex 115 | @echo "Running LaTeX files through pdflatex..." 116 | $(MAKE) -C $(BUILDDIR)/latex all-pdf 117 | @echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex." 118 | 119 | latexpdfja: 120 | $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex 121 | @echo "Running LaTeX files through platex and dvipdfmx..." 122 | $(MAKE) -C $(BUILDDIR)/latex all-pdf-ja 123 | @echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex." 124 | 125 | text: 126 | $(SPHINXBUILD) -b text $(ALLSPHINXOPTS) $(BUILDDIR)/text 127 | @echo 128 | @echo "Build finished. The text files are in $(BUILDDIR)/text." 129 | 130 | man: 131 | $(SPHINXBUILD) -b man $(ALLSPHINXOPTS) $(BUILDDIR)/man 132 | @echo 133 | @echo "Build finished. The manual pages are in $(BUILDDIR)/man." 134 | 135 | texinfo: 136 | $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo 137 | @echo 138 | @echo "Build finished. The Texinfo files are in $(BUILDDIR)/texinfo." 139 | @echo "Run \`make' in that directory to run these through makeinfo" \ 140 | "(use \`make info' here to do that automatically)." 141 | 142 | info: 143 | $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo 144 | @echo "Running Texinfo files through makeinfo..." 145 | make -C $(BUILDDIR)/texinfo info 146 | @echo "makeinfo finished; the Info files are in $(BUILDDIR)/texinfo." 147 | 148 | gettext: 149 | $(SPHINXBUILD) -b gettext $(I18NSPHINXOPTS) $(BUILDDIR)/locale 150 | @echo 151 | @echo "Build finished. The message catalogs are in $(BUILDDIR)/locale." 152 | 153 | changes: 154 | $(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) $(BUILDDIR)/changes 155 | @echo 156 | @echo "The overview file is in $(BUILDDIR)/changes." 157 | 158 | linkcheck: 159 | $(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) $(BUILDDIR)/linkcheck 160 | @echo 161 | @echo "Link check complete; look for any errors in the above output " \ 162 | "or in $(BUILDDIR)/linkcheck/output.txt." 163 | 164 | doctest: 165 | $(SPHINXBUILD) -b doctest $(ALLSPHINXOPTS) $(BUILDDIR)/doctest 166 | @echo "Testing of doctests in the sources finished, look at the " \ 167 | "results in $(BUILDDIR)/doctest/output.txt." 168 | 169 | xml: 170 | $(SPHINXBUILD) -b xml $(ALLSPHINXOPTS) $(BUILDDIR)/xml 171 | @echo 172 | @echo "Build finished. The XML files are in $(BUILDDIR)/xml." 173 | 174 | pseudoxml: 175 | $(SPHINXBUILD) -b pseudoxml $(ALLSPHINXOPTS) $(BUILDDIR)/pseudoxml 176 | @echo 177 | @echo "Build finished. The pseudo-XML files are in $(BUILDDIR)/pseudoxml." 178 | -------------------------------------------------------------------------------- /docs/_static/jupyterlab_blinky.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adafruit/circuitpython_jupyter_kernel/b4e1743ad432b1ca698f17b0e284f9e79de35f74/docs/_static/jupyterlab_blinky.gif -------------------------------------------------------------------------------- /docs/authors.rst: -------------------------------------------------------------------------------- 1 | .. include:: ../AUTHORS.rst 2 | -------------------------------------------------------------------------------- /docs/boardprep.md: -------------------------------------------------------------------------------- 1 | 2 | # Board Preparation 3 | 4 | Before you start using the CircuitPython_Kernel, you'll need a board running CircuitPython. If you're not sure if 5 | the board plugged into your computer is running CircuitPython, check your file explorer for a drive named `CIRCUITPY` 6 | 7 | ## Designed for CircuitPython (SAMD21, SAMD51 and RP2040, NXP iMXRT1062) 8 | 9 | ### Boards Supported: 10 | 11 | - [Circuit Playground Express](https://www.adafruit.com/product/3333) 12 | - [Feather M0](https://www.adafruit.com/product/3403) 13 | - [Trinket M0](https://www.adafruit.com/product/3500) 14 | - [Metro M0 Express](https://www.adafruit.com/product/3505) 15 | - [Gemma M0](https://www.adafruit.com/product/3501) 16 | - [ItsyBitsy M0](https://www.adafruit.com/product/3727) 17 | 18 | - [Metro M4 ]( https://www.adafruit.com/product/3382) 19 | - [ItsyBitsy M4](https://www.adafruit.com/product/3727) 20 | 21 | - [Raspberry Pi Pico RP2040](https://www.adafruit.com/product/4864) 22 | - [PJRC Teensy 4.1](https://www.adafruit.com/product/4622) 23 | 24 | ### Installing CircuitPython Firmware 25 | 26 | - Download the [CircuitPython Firmware (.uf2 file) from the CircuitPython Repo](https://github.com/adafruit/circuitpython/releases) 27 | - Plug in board and double click the **reset** button to enter bootloader mode. 28 | - Drag and drop the \*.uf2 CircuitPython file to the USB drive. 29 | - If you see the `CIRCUITPY` as the new name of the USB drive, you're ready to go. 30 | 31 | 32 | ## Adafruit Feather Huzzah ESP8266 33 | 34 | While they do work with CircuitPython_Kernel, ESP8266-based boards require a different type of installation and configuration 35 | from the boards designed for circuitpython. 36 | 37 | ### Installing CircuitPython Firmware 38 | 39 | - `python3 -m pip install esptool` 40 | - Download the [CircuitPython Firmware (.bin file) from the CircuitPython Repo](https://github.com/adafruit/circuitpython/releases) 41 | - Install the [SiLabs CP210x driver](https://www.silabs.com/products/development-tools/software/usb-to-uart-bridge-vcp-drivers) 42 | - Erase flash `python3 esptool.py --port /path/to/ESP8266 erase_flash` 43 | - Load firmware: `esptool.py --port /path/to/ESP8266 --baud 460800 write_flash --flash_size=detect 0 firmware.bin` 44 | - Press reset or unplug/plug the board. 45 | 46 | ### Access the REPL 47 | 48 | Use `screen` program: 49 | 50 | screen 115200 51 | 52 | ## PJRC Teensy 4.1 53 | 54 | The Teensy line of microcontrollers have a different installation to the standard circuitpython installation, requiring a program called Teensy Loader and a hex file. 55 | 56 | ### Installing CircuitPython Firmware 57 | 58 | - Download the Teensy Loader Application: https://www.pjrc.com/teensy/loader.html 59 | - Install the loader following the guide for your specific operating system. 60 | - Download the [CircuitPython Firmware (.hex file) from the CircuitPython Website](https://circuitpython.org/board/teensy41/) 61 | - Once the Teensy Loader is downloaded, press the onboard push button on the Teensy, this places the teensy in the halfkay bootlader mode. 62 | - Open the Teensy Loader Application and select the left most button and upload the downloaded .hex file. 63 | - Unplug and plug the Teensy back in and you are ready to go. 64 | 65 | ## ampy 66 | 67 | - Install ampy `python3 -m pip install adafruit-ampy` 68 | - To get options for listing files and moving files: `ampy --help` 69 | -------------------------------------------------------------------------------- /docs/circuitpython_kernel.rst: -------------------------------------------------------------------------------- 1 | circuitpython_kernel package 2 | ============================ 3 | 4 | Submodules 5 | ---------- 6 | 7 | circuitpython_kernel.board module 8 | --------------------------------- 9 | 10 | .. automodule:: circuitpython_kernel.board 11 | :members: 12 | :undoc-members: 13 | :show-inheritance: 14 | 15 | circuitpython_kernel.install module 16 | ----------------------------------- 17 | 18 | .. automodule:: circuitpython_kernel.install 19 | :members: 20 | :undoc-members: 21 | :show-inheritance: 22 | 23 | circuitpython_kernel.kernel module 24 | ---------------------------------- 25 | 26 | .. automodule:: circuitpython_kernel.kernel 27 | :members: 28 | :undoc-members: 29 | :show-inheritance: 30 | 31 | circuitpython_kernel.version module 32 | ----------------------------------- 33 | 34 | .. automodule:: circuitpython_kernel.version 35 | :members: 36 | :undoc-members: 37 | :show-inheritance: 38 | 39 | 40 | Module contents 41 | --------------- 42 | 43 | .. automodule:: circuitpython_kernel 44 | :members: 45 | :undoc-members: 46 | :show-inheritance: 47 | -------------------------------------------------------------------------------- /docs/conf.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | # 4 | # circuitpython_kernel documentation build configuration file, created by 5 | # sphinx-quickstart on Tue Jul 9 22:26:36 2013. 6 | # 7 | # This file is execfile()d with the current directory set to its 8 | # containing dir. 9 | # 10 | # Note that not all possible configuration values are present in this 11 | # autogenerated file. 12 | # 13 | # All configuration values have a default; values that are commented out 14 | # serve to show the default. 15 | 16 | import sys 17 | import os 18 | 19 | # For conversion from markdown to html 20 | import recommonmark 21 | 22 | # If extensions (or modules to document with autodoc) are in another 23 | # directory, add these directories to sys.path here. If the directory is 24 | # relative to the documentation root, use os.path.abspath to make it 25 | # absolute, like shown here. 26 | sys.path.insert(0, os.path.abspath('.')) 27 | 28 | # Get the project root dir, which is the parent dir of this 29 | cwd = os.getcwd() 30 | project_root = os.path.dirname(cwd) 31 | 32 | # Insert the project root dir as the first element in the PYTHONPATH. 33 | # This lets us ensure that the source package is imported, and that its 34 | # version is used. 35 | sys.path.insert(0, project_root) 36 | 37 | import circuitpython_kernel 38 | 39 | # -- General configuration --------------------------------------------- 40 | 41 | # If your documentation needs a minimal Sphinx version, state it here. 42 | #needs_sphinx = '1.0' 43 | 44 | # Add any Sphinx extension module names here, as strings. They can be 45 | # extensions coming with Sphinx (named 'sphinx.ext.*') or your custom ones. 46 | extensions = ['sphinx.ext.autodoc', 'sphinx.ext.viewcode', 47 | 'sphinx.ext.napoleon',] 48 | 49 | # Napoleon settings 50 | napoleon_google_docstring = False 51 | napoleon_numpy_docstring = True 52 | napoleon_include_init_with_doc = False 53 | napoleon_include_private_with_doc = False 54 | napoleon_include_special_with_doc = True 55 | napoleon_use_admonition_for_examples = False 56 | napoleon_use_admonition_for_notes = False 57 | napoleon_use_admonition_for_references = False 58 | napoleon_use_ivar = False 59 | napoleon_use_param = True 60 | napoleon_use_rtype = True 61 | 62 | # Add any paths that contain templates here, relative to this directory. 63 | templates_path = ['_templates'] 64 | 65 | source_parsers = { 66 | '.md': 'recommonmark.parser.CommonMarkParser', 67 | } 68 | 69 | # The suffix of source filenames. 70 | source_suffix = ['.rst', '.md'] 71 | 72 | # The encoding of source files. 73 | #source_encoding = 'utf-8-sig' 74 | 75 | # The master toctree document. 76 | master_doc = 'index' 77 | 78 | # General information about the project. 79 | project = u'CircuitPython Kernel' 80 | copyright = u"2018, Adafruit Industries" 81 | 82 | # The version info for the project you're documenting, acts as replacement 83 | # for |version| and |release|, also used in various other places throughout 84 | # the built documents. 85 | # 86 | # The short X.Y version. 87 | version = circuitpython_kernel.__version__ 88 | # The full version, including alpha/beta/rc tags. 89 | release = circuitpython_kernel.__version__ 90 | 91 | # The language for content autogenerated by Sphinx. Refer to documentation 92 | # for a list of supported languages. 93 | #language = None 94 | 95 | # There are two options for replacing |today|: either, you set today to 96 | # some non-false value, then it is used: 97 | #today = '' 98 | # Else, today_fmt is used as the format for a strftime call. 99 | #today_fmt = '%B %d, %Y' 100 | 101 | # List of patterns, relative to source directory, that match files and 102 | # directories to ignore when looking for source files. 103 | exclude_patterns = ['_build'] 104 | 105 | # The reST default role (used for this markup: `text`) to use for all 106 | # documents. 107 | #default_role = None 108 | 109 | # If true, '()' will be appended to :func: etc. cross-reference text. 110 | #add_function_parentheses = True 111 | 112 | # If true, the current module name will be prepended to all description 113 | # unit titles (such as .. function::). 114 | #add_module_names = True 115 | 116 | # If true, sectionauthor and moduleauthor directives will be shown in the 117 | # output. They are ignored by default. 118 | #show_authors = False 119 | 120 | # The name of the Pygments (syntax highlighting) style to use. 121 | pygments_style = 'sphinx' 122 | 123 | # A list of ignored prefixes for module index sorting. 124 | #modindex_common_prefix = [] 125 | 126 | # If true, keep warnings as "system message" paragraphs in the built 127 | # documents. 128 | #keep_warnings = False 129 | 130 | 131 | # -- Options for HTML output ------------------------------------------- 132 | 133 | # The theme to use for HTML and HTML Help pages. See the documentation for 134 | # a list of builtin themes. 135 | html_theme = 'flask' 136 | 137 | # Theme options are theme-specific and customize the look and feel of a 138 | # theme further. For a list of options available for each theme, see the 139 | # documentation. 140 | html_theme_options = { 141 | 'github_fork': 'adafruit/circuitpython_kernel', 142 | } 143 | 144 | # Add any paths that contain custom themes here, relative to this directory. 145 | #html_theme_path = [] 146 | 147 | # The name for this set of Sphinx documents. If None, it defaults to 148 | # " v documentation". 149 | #html_title = None 150 | 151 | # A shorter title for the navigation bar. Default is the same as 152 | # html_title. 153 | #html_short_title = None 154 | 155 | # The name of an image file (relative to this directory) to place at the 156 | # top of the sidebar. 157 | #html_logo = None 158 | 159 | # The name of an image file (within the static path) to use as favicon 160 | # of the docs. This file should be a Windows icon file (.ico) being 161 | # 16x16 or 32x32 pixels large. 162 | #html_favicon = None 163 | 164 | # Add any paths that contain custom static files (such as style sheets) 165 | # here, relative to this directory. They are copied after the builtin 166 | # static files, so a file named "default.css" will overwrite the builtin 167 | # "default.css". 168 | html_static_path = ['_static'] 169 | 170 | # If not '', a 'Last updated on:' timestamp is inserted at every page 171 | # bottom, using the given strftime format. 172 | #html_last_updated_fmt = '%b %d, %Y' 173 | 174 | # If true, SmartyPants will be used to convert quotes and dashes to 175 | # typographically correct entities. 176 | #html_use_smartypants = True 177 | 178 | # Custom sidebar templates, maps document names to template names. 179 | #html_sidebars = {} 180 | 181 | # Additional templates that should be rendered to pages, maps page names 182 | # to template names. 183 | #html_additional_pages = {} 184 | 185 | # If false, no module index is generated. 186 | #html_domain_indices = True 187 | 188 | # If false, no index is generated. 189 | #html_use_index = True 190 | 191 | # If true, the index is split into individual pages for each letter. 192 | #html_split_index = False 193 | 194 | # If true, links to the reST sources are added to the pages. 195 | #html_show_sourcelink = True 196 | 197 | # If true, "Created using Sphinx" is shown in the HTML footer. 198 | # Default is True. 199 | #html_show_sphinx = True 200 | 201 | # If true, "(C) Copyright ..." is shown in the HTML footer. 202 | # Default is True. 203 | #html_show_copyright = True 204 | 205 | # If true, an OpenSearch description file will be output, and all pages 206 | # will contain a tag referring to it. The value of this option 207 | # must be the base URL from which the finished HTML is served. 208 | #html_use_opensearch = '' 209 | 210 | # This is the file name suffix for HTML files (e.g. ".xhtml"). 211 | #html_file_suffix = None 212 | 213 | # Output file base name for HTML help builder. 214 | htmlhelp_basename = 'circuitpython_kerneldoc' 215 | 216 | 217 | # -- Options for LaTeX output ------------------------------------------ 218 | 219 | latex_elements = { 220 | # The paper size ('letterpaper' or 'a4paper'). 221 | #'papersize': 'letterpaper', 222 | 223 | # The font size ('10pt', '11pt' or '12pt'). 224 | #'pointsize': '10pt', 225 | 226 | # Additional stuff for the LaTeX preamble. 227 | #'preamble': '', 228 | } 229 | 230 | # Grouping the document tree into LaTeX files. List of tuples 231 | # (source start file, target name, title, author, documentclass 232 | # [howto/manual]). 233 | latex_documents = [ 234 | ('index', 'circuitpython_kernel.tex', 235 | u'CircuitPython Kernel Documentation', 236 | u'Adafruit Industries', 'manual'), 237 | ] 238 | 239 | # The name of an image file (relative to this directory) to place at 240 | # the top of the title page. 241 | #latex_logo = None 242 | 243 | # For "manual" documents, if this is true, then toplevel headings 244 | # are parts, not chapters. 245 | #latex_use_parts = False 246 | 247 | # If true, show page references after internal links. 248 | #latex_show_pagerefs = False 249 | 250 | # If true, show URL addresses after external links. 251 | #latex_show_urls = False 252 | 253 | # Documents to append as an appendix to all manuals. 254 | #latex_appendices = [] 255 | 256 | # If false, no module index is generated. 257 | #latex_domain_indices = True 258 | 259 | 260 | # -- Options for manual page output ------------------------------------ 261 | 262 | # One entry per manual page. List of tuples 263 | # (source start file, name, description, authors, manual section). 264 | man_pages = [ 265 | ('index', 'circuitpython_kernel', 266 | u'CircuitPython Kernel Documentation', 267 | [u'Adafruit Industries'], 1) 268 | ] 269 | 270 | # If true, show URL addresses after external links. 271 | #man_show_urls = False 272 | 273 | 274 | # -- Options for Texinfo output ---------------------------------------- 275 | 276 | # Grouping the document tree into Texinfo files. List of tuples 277 | # (source start file, target name, title, author, 278 | # dir menu entry, description, category) 279 | texinfo_documents = [ 280 | ('index', 'circuitpython_kernel', 281 | u'CircuitPython Kernel Documentation', 282 | u'Adafruit Industries', 283 | 'circuitpython_kernel', 284 | 'One line description of project.', 285 | 'Miscellaneous'), 286 | ] 287 | 288 | # Documents to append as an appendix to all manuals. 289 | #texinfo_appendices = [] 290 | 291 | # If false, no module index is generated. 292 | #texinfo_domain_indices = True 293 | 294 | # How to display URL addresses: 'footnote', 'no', or 'inline'. 295 | #texinfo_show_urls = 'footnote' 296 | 297 | # If true, do not generate a @detailmenu in the "Top" node's menu. 298 | #texinfo_no_detailmenu = False 299 | -------------------------------------------------------------------------------- /docs/contributing.rst: -------------------------------------------------------------------------------- 1 | .. include:: ../CONTRIBUTING.rst 2 | -------------------------------------------------------------------------------- /docs/environment.yml: -------------------------------------------------------------------------------- 1 | name: cpy_docs 2 | channels: 3 | - conda-forge 4 | dependencies: 5 | - python=3.6 6 | - ipykernel 7 | - sphinx>=1.3.6 8 | - pip: 9 | - circuitpython_kernel>=0.2.0 10 | - Flask-Sphinx-Themes 11 | - recommonmark 12 | -------------------------------------------------------------------------------- /docs/history.rst: -------------------------------------------------------------------------------- 1 | .. include:: ../HISTORY.rst 2 | -------------------------------------------------------------------------------- /docs/index.rst: -------------------------------------------------------------------------------- 1 | CircuitPython Kernel 2 | ==================== 3 | 4 | Contents: 5 | 6 | .. toctree:: 7 | :maxdepth: 2 8 | 9 | readme 10 | installation 11 | usage 12 | contributing 13 | authors 14 | history 15 | 16 | .. toctree:: 17 | :maxdepth: 2 18 | :caption: Hardware 19 | 20 | boardprep 21 | 22 | Indices and tables 23 | ================== 24 | 25 | * :ref:`genindex` 26 | * :ref:`modindex` 27 | * :ref:`search` 28 | -------------------------------------------------------------------------------- /docs/installation.rst: -------------------------------------------------------------------------------- 1 | .. highlight:: shell 2 | 3 | Installing Jupyter 4 | ------------------ 5 | 6 | Option 1. Installing Jupyter with Anaconda 7 | 8 | **Don't have a Python installation on your computer?** If you're new to all this, the Jupyter Project recommends installing `Anaconda `_ 9 | , which installs Python, the Jupyter Notebook, and other commonly used packages for scientific computing and data science. 10 | 11 | Option 2. Installing Jupyter with PIP 12 | 13 | If you have a Python installation already on your computer, you may want to use the Python package manager (pip) instead of Anaconda. You'll need Python 3.3+. 14 | 15 | First, ensure that you have the latest version of pip: 16 | 17 | .. code:: shell 18 | 19 | $ pip3 install --upgrade pip 20 | 21 | 22 | Install the Jupyter Notebook using: 23 | 24 | .. code:: shell 25 | 26 | $ pip3 install jupyter 27 | 28 | Ok, now that we have Jupyter installed, let's start the notebook server. 29 | 30 | We can launch the server from a command line (either Terminal on macOS/Linux or Command Prompt on Windows) by running: 31 | 32 | .. code:: shell 33 | 34 | $ jupyter notebook 35 | 36 | If your installation went well, you'll see information about the notebook server in your command line. Also, your web browser will open to the URL displayed in your command line (http://localhost:8888), displaying the Notebook Dashboard. 37 | 38 | Installing the CircuitPython Kernel 39 | ----------------------------------- 40 | First, clone this repository. 41 | 42 | .. code:: shell 43 | 44 | $ git clone https://github.com/adafruit/circuitpython_kernel.git 45 | 46 | Navigate into the cloned repository directory. Install this kernel into Jupyter by running: 47 | 48 | .. code:: shell 49 | 50 | $ python3 setup.py install 51 | 52 | Then, run 53 | 54 | .. code:: shell 55 | 56 | $ python3 -m circuitpython_kernel.install 57 | 58 | * if you encounter errors running this command on macOS/Linux, you'll need to prefix this command with *sudo* 59 | 60 | Finally, let's verify the kernel was installed correctly in Jupyter. To do this, run: 61 | 62 | 63 | .. code:: shell 64 | 65 | $ jupyter kernelspec list 66 | 67 | Your output should show circuitpython as an available kernel: 68 | 69 | .. image:: https://cdn-learn.adafruit.com/assets/assets/000/055/226/original/circuitpython_jupyter-kernelspec-list.png?1528483983 70 | 71 | 72 | .. _GitHub repo: https://github.com/adafruit/circuitpython_kernel 73 | .. _tarball: https://github.com/adafruit/circuitpython_kernel/tarball/master 74 | -------------------------------------------------------------------------------- /docs/make.bat: -------------------------------------------------------------------------------- 1 | @ECHO OFF 2 | 3 | REM Command file for Sphinx documentation 4 | 5 | if "%SPHINXBUILD%" == "" ( 6 | set SPHINXBUILD=sphinx-build 7 | ) 8 | set BUILDDIR=_build 9 | set ALLSPHINXOPTS=-d %BUILDDIR%/doctrees %SPHINXOPTS% . 10 | set I18NSPHINXOPTS=%SPHINXOPTS% . 11 | if NOT "%PAPER%" == "" ( 12 | set ALLSPHINXOPTS=-D latex_paper_size=%PAPER% %ALLSPHINXOPTS% 13 | set I18NSPHINXOPTS=-D latex_paper_size=%PAPER% %I18NSPHINXOPTS% 14 | ) 15 | 16 | if "%1" == "" goto help 17 | 18 | if "%1" == "help" ( 19 | :help 20 | echo.Please use `make ^` where ^ is one of 21 | echo. html to make standalone HTML files 22 | echo. dirhtml to make HTML files named index.html in directories 23 | echo. singlehtml to make a single large HTML file 24 | echo. pickle to make pickle files 25 | echo. json to make JSON files 26 | echo. htmlhelp to make HTML files and a HTML help project 27 | echo. qthelp to make HTML files and a qthelp project 28 | echo. devhelp to make HTML files and a Devhelp project 29 | echo. epub to make an epub 30 | echo. latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter 31 | echo. text to make text files 32 | echo. man to make manual pages 33 | echo. texinfo to make Texinfo files 34 | echo. gettext to make PO message catalogs 35 | echo. changes to make an overview over all changed/added/deprecated items 36 | echo. xml to make Docutils-native XML files 37 | echo. pseudoxml to make pseudoxml-XML files for display purposes 38 | echo. linkcheck to check all external links for integrity 39 | echo. doctest to run all doctests embedded in the documentation if enabled 40 | goto end 41 | ) 42 | 43 | if "%1" == "clean" ( 44 | for /d %%i in (%BUILDDIR%\*) do rmdir /q /s %%i 45 | del /q /s %BUILDDIR%\* 46 | goto end 47 | ) 48 | 49 | 50 | %SPHINXBUILD% 2> nul 51 | if errorlevel 9009 ( 52 | echo. 53 | echo.The 'sphinx-build' command was not found. Make sure you have Sphinx 54 | echo.installed, then set the SPHINXBUILD environment variable to point 55 | echo.to the full path of the 'sphinx-build' executable. Alternatively you 56 | echo.may add the Sphinx directory to PATH. 57 | echo. 58 | echo.If you don't have Sphinx installed, grab it from 59 | echo.http://sphinx-doc.org/ 60 | exit /b 1 61 | ) 62 | 63 | if "%1" == "html" ( 64 | %SPHINXBUILD% -b html %ALLSPHINXOPTS% %BUILDDIR%/html 65 | if errorlevel 1 exit /b 1 66 | echo. 67 | echo.Build finished. The HTML pages are in %BUILDDIR%/html. 68 | goto end 69 | ) 70 | 71 | if "%1" == "dirhtml" ( 72 | %SPHINXBUILD% -b dirhtml %ALLSPHINXOPTS% %BUILDDIR%/dirhtml 73 | if errorlevel 1 exit /b 1 74 | echo. 75 | echo.Build finished. The HTML pages are in %BUILDDIR%/dirhtml. 76 | goto end 77 | ) 78 | 79 | if "%1" == "singlehtml" ( 80 | %SPHINXBUILD% -b singlehtml %ALLSPHINXOPTS% %BUILDDIR%/singlehtml 81 | if errorlevel 1 exit /b 1 82 | echo. 83 | echo.Build finished. The HTML pages are in %BUILDDIR%/singlehtml. 84 | goto end 85 | ) 86 | 87 | if "%1" == "pickle" ( 88 | %SPHINXBUILD% -b pickle %ALLSPHINXOPTS% %BUILDDIR%/pickle 89 | if errorlevel 1 exit /b 1 90 | echo. 91 | echo.Build finished; now you can process the pickle files. 92 | goto end 93 | ) 94 | 95 | if "%1" == "json" ( 96 | %SPHINXBUILD% -b json %ALLSPHINXOPTS% %BUILDDIR%/json 97 | if errorlevel 1 exit /b 1 98 | echo. 99 | echo.Build finished; now you can process the JSON files. 100 | goto end 101 | ) 102 | 103 | if "%1" == "htmlhelp" ( 104 | %SPHINXBUILD% -b htmlhelp %ALLSPHINXOPTS% %BUILDDIR%/htmlhelp 105 | if errorlevel 1 exit /b 1 106 | echo. 107 | echo.Build finished; now you can run HTML Help Workshop with the ^ 108 | .hhp project file in %BUILDDIR%/htmlhelp. 109 | goto end 110 | ) 111 | 112 | if "%1" == "qthelp" ( 113 | %SPHINXBUILD% -b qthelp %ALLSPHINXOPTS% %BUILDDIR%/qthelp 114 | if errorlevel 1 exit /b 1 115 | echo. 116 | echo.Build finished; now you can run "qcollectiongenerator" with the ^ 117 | .qhcp project file in %BUILDDIR%/qthelp, like this: 118 | echo.^> qcollectiongenerator %BUILDDIR%\qthelp\circuitpython_kernel.qhcp 119 | echo.To view the help file: 120 | echo.^> assistant -collectionFile %BUILDDIR%\qthelp\circuitpython_kernel.ghc 121 | goto end 122 | ) 123 | 124 | if "%1" == "devhelp" ( 125 | %SPHINXBUILD% -b devhelp %ALLSPHINXOPTS% %BUILDDIR%/devhelp 126 | if errorlevel 1 exit /b 1 127 | echo. 128 | echo.Build finished. 129 | goto end 130 | ) 131 | 132 | if "%1" == "epub" ( 133 | %SPHINXBUILD% -b epub %ALLSPHINXOPTS% %BUILDDIR%/epub 134 | if errorlevel 1 exit /b 1 135 | echo. 136 | echo.Build finished. The epub file is in %BUILDDIR%/epub. 137 | goto end 138 | ) 139 | 140 | if "%1" == "latex" ( 141 | %SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex 142 | if errorlevel 1 exit /b 1 143 | echo. 144 | echo.Build finished; the LaTeX files are in %BUILDDIR%/latex. 145 | goto end 146 | ) 147 | 148 | if "%1" == "latexpdf" ( 149 | %SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex 150 | cd %BUILDDIR%/latex 151 | make all-pdf 152 | cd %BUILDDIR%/.. 153 | echo. 154 | echo.Build finished; the PDF files are in %BUILDDIR%/latex. 155 | goto end 156 | ) 157 | 158 | if "%1" == "latexpdfja" ( 159 | %SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex 160 | cd %BUILDDIR%/latex 161 | make all-pdf-ja 162 | cd %BUILDDIR%/.. 163 | echo. 164 | echo.Build finished; the PDF files are in %BUILDDIR%/latex. 165 | goto end 166 | ) 167 | 168 | if "%1" == "text" ( 169 | %SPHINXBUILD% -b text %ALLSPHINXOPTS% %BUILDDIR%/text 170 | if errorlevel 1 exit /b 1 171 | echo. 172 | echo.Build finished. The text files are in %BUILDDIR%/text. 173 | goto end 174 | ) 175 | 176 | if "%1" == "man" ( 177 | %SPHINXBUILD% -b man %ALLSPHINXOPTS% %BUILDDIR%/man 178 | if errorlevel 1 exit /b 1 179 | echo. 180 | echo.Build finished. The manual pages are in %BUILDDIR%/man. 181 | goto end 182 | ) 183 | 184 | if "%1" == "texinfo" ( 185 | %SPHINXBUILD% -b texinfo %ALLSPHINXOPTS% %BUILDDIR%/texinfo 186 | if errorlevel 1 exit /b 1 187 | echo. 188 | echo.Build finished. The Texinfo files are in %BUILDDIR%/texinfo. 189 | goto end 190 | ) 191 | 192 | if "%1" == "gettext" ( 193 | %SPHINXBUILD% -b gettext %I18NSPHINXOPTS% %BUILDDIR%/locale 194 | if errorlevel 1 exit /b 1 195 | echo. 196 | echo.Build finished. The message catalogs are in %BUILDDIR%/locale. 197 | goto end 198 | ) 199 | 200 | if "%1" == "changes" ( 201 | %SPHINXBUILD% -b changes %ALLSPHINXOPTS% %BUILDDIR%/changes 202 | if errorlevel 1 exit /b 1 203 | echo. 204 | echo.The overview file is in %BUILDDIR%/changes. 205 | goto end 206 | ) 207 | 208 | if "%1" == "linkcheck" ( 209 | %SPHINXBUILD% -b linkcheck %ALLSPHINXOPTS% %BUILDDIR%/linkcheck 210 | if errorlevel 1 exit /b 1 211 | echo. 212 | echo.Link check complete; look for any errors in the above output ^ 213 | or in %BUILDDIR%/linkcheck/output.txt. 214 | goto end 215 | ) 216 | 217 | if "%1" == "doctest" ( 218 | %SPHINXBUILD% -b doctest %ALLSPHINXOPTS% %BUILDDIR%/doctest 219 | if errorlevel 1 exit /b 1 220 | echo. 221 | echo.Testing of doctests in the sources finished, look at the ^ 222 | results in %BUILDDIR%/doctest/output.txt. 223 | goto end 224 | ) 225 | 226 | if "%1" == "xml" ( 227 | %SPHINXBUILD% -b xml %ALLSPHINXOPTS% %BUILDDIR%/xml 228 | if errorlevel 1 exit /b 1 229 | echo. 230 | echo.Build finished. The XML files are in %BUILDDIR%/xml. 231 | goto end 232 | ) 233 | 234 | if "%1" == "pseudoxml" ( 235 | %SPHINXBUILD% -b pseudoxml %ALLSPHINXOPTS% %BUILDDIR%/pseudoxml 236 | if errorlevel 1 exit /b 1 237 | echo. 238 | echo.Build finished. The pseudo-XML files are in %BUILDDIR%/pseudoxml. 239 | goto end 240 | ) 241 | 242 | :end 243 | -------------------------------------------------------------------------------- /docs/modules.rst: -------------------------------------------------------------------------------- 1 | circuitpython_kernel 2 | ==================== 3 | 4 | .. toctree:: 5 | :maxdepth: 4 6 | 7 | circuitpython_kernel 8 | -------------------------------------------------------------------------------- /docs/readme.rst: -------------------------------------------------------------------------------- 1 | .. include:: ../README.rst 2 | -------------------------------------------------------------------------------- /docs/usage.rst: -------------------------------------------------------------------------------- 1 | .. highlight:: shell 2 | 3 | Launching a CircuitPython Notebook 4 | ---------------------------------- 5 | Launch jupyter by running: 6 | 7 | .. code:: shell 8 | 9 | $ jupyter notebook 10 | 11 | Make sure your board is plugged into USB and running CircuitPython by opening a file explorer. It should show up as a removable drive named **CIRCUITPY**. 12 | 13 | Then click **new -> circuitpython** to open a new CircuitPython Notebook 14 | 15 | .. image:: https://cdn-learn.adafruit.com/assets/assets/000/055/305/original/circuitpython_newnotebook.gif?1528755209 16 | 17 | A new CircuitPython Notebook should open and you should be able to execute code from within a cell. 18 | -------------------------------------------------------------------------------- /examples/CPX_Blink_Pico.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "metadata": {}, 6 | "source": [ 7 | "# Blinking a LED\n", 8 | "We're going to be blinking the LED on your Raspberry Pi Pico, right from this Jupyter Notebook!\n", 9 | "\n", 10 | "![cpx led](https://cdn-learn.adafruit.com/assets/assets/000/098/848/large1024/raspberry_pi_circuitpython_RP_01_top_ORIG_2021_01b.jpg?1611338794)" 11 | ] 12 | }, 13 | { 14 | "cell_type": "markdown", 15 | "metadata": {}, 16 | "source": [ 17 | "Let's first check if Jupyter can read our CircuitPython board by running:" 18 | ] 19 | }, 20 | { 21 | "cell_type": "code", 22 | "execution_count": null, 23 | "metadata": {}, 24 | "outputs": [], 25 | "source": [ 26 | "import os\n", 27 | "print(os.uname())" 28 | ] 29 | }, 30 | { 31 | "cell_type": "markdown", 32 | "metadata": {}, 33 | "source": [ 34 | "First, import `board` and `digitalio` " 35 | ] 36 | }, 37 | { 38 | "cell_type": "code", 39 | "execution_count": null, 40 | "metadata": {}, 41 | "outputs": [], 42 | "source": [ 43 | "import digitalio\n", 44 | "import board" 45 | ] 46 | }, 47 | { 48 | "cell_type": "markdown", 49 | "metadata": {}, 50 | "source": [ 51 | "Create a `digitalio.DigitalInOut` object for the LED in the Raspberry Pi Pico:" 52 | ] 53 | }, 54 | { 55 | "cell_type": "code", 56 | "execution_count": null, 57 | "metadata": {}, 58 | "outputs": [], 59 | "source": [ 60 | "cpx_led = digitalio.DigitalInOut(board.LED)" 61 | ] 62 | }, 63 | { 64 | "cell_type": "markdown", 65 | "metadata": {}, 66 | "source": [ 67 | "Let's change the direction of `digitialio.Direction` to be an `OUTPUT`" 68 | ] 69 | }, 70 | { 71 | "cell_type": "code", 72 | "execution_count": null, 73 | "metadata": {}, 74 | "outputs": [], 75 | "source": [ 76 | "cpx_led.direction = digitalio.Direction.OUTPUT" 77 | ] 78 | }, 79 | { 80 | "cell_type": "markdown", 81 | "metadata": {}, 82 | "source": [ 83 | "Finally, let's turn it on by changing the value of the pin to `True` " 84 | ] 85 | }, 86 | { 87 | "cell_type": "code", 88 | "execution_count": null, 89 | "metadata": {}, 90 | "outputs": [], 91 | "source": [ 92 | "cpx_led.value = True" 93 | ] 94 | }, 95 | { 96 | "cell_type": "markdown", 97 | "metadata": {}, 98 | "source": [ 99 | "Don't want the LED on? Turn it off by switching the value from `True` to `False`" 100 | ] 101 | }, 102 | { 103 | "cell_type": "code", 104 | "execution_count": null, 105 | "metadata": {}, 106 | "outputs": [], 107 | "source": [ 108 | "cpx_led.value = False" 109 | ] 110 | }, 111 | { 112 | "cell_type": "code", 113 | "execution_count": null, 114 | "metadata": {}, 115 | "outputs": [], 116 | "source": [] 117 | } 118 | ], 119 | "metadata": { 120 | "kernelspec": { 121 | "display_name": "CircuitPython", 122 | "language": "python", 123 | "name": "circuitpython" 124 | }, 125 | "language_info": { 126 | "codemirror_mode": { 127 | "name": "python", 128 | "version": 3 129 | }, 130 | "file_extension": ".py", 131 | "mimetype": "text/x-python", 132 | "name": "python", 133 | "pygments_lexer": "python3", 134 | "version": "3" 135 | } 136 | }, 137 | "nbformat": 4, 138 | "nbformat_minor": 2 139 | } 140 | -------------------------------------------------------------------------------- /examples/CPX_Blink_Teensy.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "id": "double-loading", 6 | "metadata": {}, 7 | "source": [ 8 | "## Blinking an LED" 9 | ] 10 | }, 11 | { 12 | "cell_type": "markdown", 13 | "id": "increasing-booth", 14 | "metadata": {}, 15 | "source": [ 16 | "The traditional Hello World in Microcontrollers is the blinking of the onboard LED. This can be done using this Jupyter Notebook." 17 | ] 18 | }, 19 | { 20 | "cell_type": "markdown", 21 | "id": "colonial-ratio", 22 | "metadata": {}, 23 | "source": [ 24 | "![cpx led](https://cdn-shop.adafruit.com/970x728/4622-03.jpg)" 25 | ] 26 | }, 27 | { 28 | "cell_type": "markdown", 29 | "id": "guilty-cincinnati", 30 | "metadata": {}, 31 | "source": [ 32 | "To Begin let's make sure the kernel in the top left hand corner says CircuitPython. This can be modified by going to the kernel tab and selecting change kernel.\n", 33 | "\n", 34 | "Once this is done we can confirm that Jupyter can read our circuit python board by running:" 35 | ] 36 | }, 37 | { 38 | "cell_type": "code", 39 | "execution_count": null, 40 | "id": "protecting-cache", 41 | "metadata": {}, 42 | "outputs": [], 43 | "source": [ 44 | "import os\n", 45 | "print(os.uname())" 46 | ] 47 | }, 48 | { 49 | "cell_type": "markdown", 50 | "id": "individual-hybrid", 51 | "metadata": {}, 52 | "source": [ 53 | "Next to be able to turn on and off the LEDs we import board and digitalIO. Board allows access to the specific pins on the Teensy. DigitalIO creates objects for IO based programming." 54 | ] 55 | }, 56 | { 57 | "cell_type": "code", 58 | "execution_count": 6, 59 | "id": "balanced-steps", 60 | "metadata": {}, 61 | "outputs": [], 62 | "source": [ 63 | "import digitalio\n", 64 | "import board" 65 | ] 66 | }, 67 | { 68 | "cell_type": "markdown", 69 | "id": "breeding-supervision", 70 | "metadata": {}, 71 | "source": [ 72 | "Create the digitalio.DigitalInOut object for the LED in the Teensy (Teensy's onboard LED is at D13)" 73 | ] 74 | }, 75 | { 76 | "cell_type": "code", 77 | "execution_count": 7, 78 | "id": "decreased-little", 79 | "metadata": {}, 80 | "outputs": [], 81 | "source": [ 82 | "cpx_led = digitalio.DigitalInOut(board.D13)" 83 | ] 84 | }, 85 | { 86 | "cell_type": "markdown", 87 | "id": "gorgeous-first", 88 | "metadata": {}, 89 | "source": [ 90 | "Let's change the direction of digitialio.Direction to be an OUTPUT" 91 | ] 92 | }, 93 | { 94 | "cell_type": "code", 95 | "execution_count": 8, 96 | "id": "satellite-recycling", 97 | "metadata": {}, 98 | "outputs": [], 99 | "source": [ 100 | "cpx_led.direction = digitalio.Direction.OUTPUT" 101 | ] 102 | }, 103 | { 104 | "cell_type": "markdown", 105 | "id": "arabic-aluminum", 106 | "metadata": {}, 107 | "source": [ 108 | "Finally, let's turn it on by changing the value of the pin to True" 109 | ] 110 | }, 111 | { 112 | "cell_type": "code", 113 | "execution_count": 9, 114 | "id": "increased-target", 115 | "metadata": {}, 116 | "outputs": [], 117 | "source": [ 118 | "cpx_led.value = True" 119 | ] 120 | }, 121 | { 122 | "cell_type": "markdown", 123 | "id": "union-lease", 124 | "metadata": {}, 125 | "source": [ 126 | "Don't want the LED on? Turn it off by switching the value from True to False" 127 | ] 128 | }, 129 | { 130 | "cell_type": "code", 131 | "execution_count": 10, 132 | "id": "noble-record", 133 | "metadata": {}, 134 | "outputs": [], 135 | "source": [ 136 | "cpx_led.value = False" 137 | ] 138 | } 139 | ], 140 | "metadata": { 141 | "kernelspec": { 142 | "display_name": "CircuitPython", 143 | "language": "python", 144 | "name": "circuitpython" 145 | }, 146 | "language_info": { 147 | "codemirror_mode": { 148 | "name": "python", 149 | "version": 3 150 | }, 151 | "file_extension": ".py", 152 | "mimetype": "text/x-python", 153 | "name": "python", 154 | "pygments_lexer": "python3", 155 | "version": "3" 156 | } 157 | }, 158 | "nbformat": 4, 159 | "nbformat_minor": 5 160 | } 161 | -------------------------------------------------------------------------------- /examples/CPX_Blinka.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "metadata": {}, 6 | "source": [ 7 | "# Blinking a LED\n", 8 | "We're going to be blinking the LED on your Adafruit Circuit Playground Express, right from this Jupyter Notebook!\n", 9 | "\n", 10 | "![cpx led](https://i.imgur.com/ObPutOO.png)" 11 | ] 12 | }, 13 | { 14 | "cell_type": "markdown", 15 | "metadata": {}, 16 | "source": [ 17 | "Let's first check if Jupyter can read our CircuitPython board by running:" 18 | ] 19 | }, 20 | { 21 | "cell_type": "code", 22 | "execution_count": null, 23 | "metadata": {}, 24 | "outputs": [], 25 | "source": [ 26 | "import os\n", 27 | "print(os.uname())" 28 | ] 29 | }, 30 | { 31 | "cell_type": "markdown", 32 | "metadata": {}, 33 | "source": [ 34 | "First, import `board` and `digitalio` " 35 | ] 36 | }, 37 | { 38 | "cell_type": "code", 39 | "execution_count": null, 40 | "metadata": {}, 41 | "outputs": [], 42 | "source": [ 43 | "import digitalio\n", 44 | "import board" 45 | ] 46 | }, 47 | { 48 | "cell_type": "markdown", 49 | "metadata": {}, 50 | "source": [ 51 | "Create a `digitalio.DigitalInOut` object for the LED, it's located at Digitial Pin 13 on the Circuit Playground Express:" 52 | ] 53 | }, 54 | { 55 | "cell_type": "code", 56 | "execution_count": null, 57 | "metadata": {}, 58 | "outputs": [], 59 | "source": [ 60 | "cpx_led = digitalio.DigitalInOut(board.D13)" 61 | ] 62 | }, 63 | { 64 | "cell_type": "markdown", 65 | "metadata": {}, 66 | "source": [ 67 | "Let's change the direction of `digitialio.Direction` to be an `OUTPUT`" 68 | ] 69 | }, 70 | { 71 | "cell_type": "code", 72 | "execution_count": null, 73 | "metadata": {}, 74 | "outputs": [], 75 | "source": [ 76 | "cpx_led.direction = digitalio.Direction.OUTPUT" 77 | ] 78 | }, 79 | { 80 | "cell_type": "markdown", 81 | "metadata": {}, 82 | "source": [ 83 | "Finally, let's turn it on by changing the value of the pin to `True` " 84 | ] 85 | }, 86 | { 87 | "cell_type": "code", 88 | "execution_count": null, 89 | "metadata": {}, 90 | "outputs": [], 91 | "source": [ 92 | "cpx_led.value = True" 93 | ] 94 | }, 95 | { 96 | "cell_type": "markdown", 97 | "metadata": {}, 98 | "source": [ 99 | "Don't want the LED on? Turn it off by switching the value from `True` to `False`" 100 | ] 101 | }, 102 | { 103 | "cell_type": "code", 104 | "execution_count": null, 105 | "metadata": {}, 106 | "outputs": [], 107 | "source": [ 108 | "cpx_led.value = False" 109 | ] 110 | }, 111 | { 112 | "cell_type": "code", 113 | "execution_count": null, 114 | "metadata": {}, 115 | "outputs": [], 116 | "source": [] 117 | } 118 | ], 119 | "metadata": { 120 | "kernelspec": { 121 | "display_name": "CircuitPython", 122 | "language": "python", 123 | "name": "circuitpython" 124 | }, 125 | "language_info": { 126 | "codemirror_mode": { 127 | "name": "python", 128 | "version": 3 129 | }, 130 | "file_extension": ".py", 131 | "mimetype": "text/x-python", 132 | "name": "python", 133 | "pygments_lexer": "python3", 134 | "version": "3" 135 | } 136 | }, 137 | "nbformat": 4, 138 | "nbformat_minor": 2 139 | } 140 | -------------------------------------------------------------------------------- /examples/CPX_NeoPixels.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "metadata": {}, 6 | "source": [ 7 | "# CircuitPython NeoPixel\n", 8 | "![NeoPixel-CPX](https://cdn-learn.adafruit.com/assets/assets/000/047/260/medium800/circuit_playground_neopix.jpg?1507908659)\n", 9 | "NeoPixels are a revolutionary and ultra-popular way to add lights and color to your project. These stranded RGB lights have the controller inside the LED, so you just push the RGB data and the LEDs do all the work for you! They're a perfect match for CircuitPython." 10 | ] 11 | }, 12 | { 13 | "cell_type": "markdown", 14 | "metadata": {}, 15 | "source": [ 16 | "Let's first check if Jupyter can read our CircuitPython board:" 17 | ] 18 | }, 19 | { 20 | "cell_type": "code", 21 | "execution_count": null, 22 | "metadata": {}, 23 | "outputs": [], 24 | "source": [ 25 | "import os\n", 26 | "print(os.uname())" 27 | ] 28 | }, 29 | { 30 | "cell_type": "markdown", 31 | "metadata": {}, 32 | "source": [ 33 | "First, we'll want to import the `time` and `board` pin definitions" 34 | ] 35 | }, 36 | { 37 | "cell_type": "code", 38 | "execution_count": null, 39 | "metadata": {}, 40 | "outputs": [], 41 | "source": [ 42 | "import time\n", 43 | "import board" 44 | ] 45 | }, 46 | { 47 | "cell_type": "markdown", 48 | "metadata": {}, 49 | "source": [ 50 | "Next, we'll want to import the neopixel library by running:" 51 | ] 52 | }, 53 | { 54 | "cell_type": "code", 55 | "execution_count": null, 56 | "metadata": {}, 57 | "outputs": [], 58 | "source": [ 59 | "import neopixel" 60 | ] 61 | }, 62 | { 63 | "cell_type": "markdown", 64 | "metadata": {}, 65 | "source": [ 66 | "Did you receive an error: `ImportError: no module named 'neopixel'`? [You'll need to install the neopixel.mpy library if you don't have it yet.](https://github.com/adafruit/Adafruit_CircuitPython_Bundle/releases/latest)" 67 | ] 68 | }, 69 | { 70 | "cell_type": "markdown", 71 | "metadata": {}, 72 | "source": [ 73 | "Let's create a `pixels` object and configure it with the ten builtin neopixels on the Circuit Playground Express" 74 | ] 75 | }, 76 | { 77 | "cell_type": "code", 78 | "execution_count": null, 79 | "metadata": {}, 80 | "outputs": [], 81 | "source": [ 82 | "pixels = neopixel.NeoPixel(board.NEOPIXEL, 10, brightness=.2)\n", 83 | "pixels.fill((0, 0, 0))\n", 84 | "pixels.show()" 85 | ] 86 | }, 87 | { 88 | "cell_type": "markdown", 89 | "metadata": {}, 90 | "source": [ 91 | "Ok, now let's make the neopixel ring flash by making a function called 'ring_flash'`" 92 | ] 93 | }, 94 | { 95 | "cell_type": "code", 96 | "execution_count": null, 97 | "metadata": {}, 98 | "outputs": [], 99 | "source": [ 100 | "def flash_pixels(flash_speed=0.5):\n", 101 | " print('flashing R')\n", 102 | " pixels.fill((255, 0, 0))\n", 103 | " pixels.show()\n", 104 | " time.sleep(flash_speed)\n", 105 | " \n", 106 | " print('flashing G')\n", 107 | " pixels.fill((0, 255, 0))\n", 108 | " pixels.show()\n", 109 | " time.sleep(flash_speed)\n", 110 | " \n", 111 | " print('flashing B')\n", 112 | " pixels.fill((0, 0, 255))\n", 113 | " pixels.show()\n", 114 | " time.sleep(flash_speed)" 115 | ] 116 | }, 117 | { 118 | "cell_type": "markdown", 119 | "metadata": {}, 120 | "source": [ 121 | "We can increase the speed of the NeoPixels by definining a `flash_speed`" 122 | ] 123 | }, 124 | { 125 | "cell_type": "code", 126 | "execution_count": null, 127 | "metadata": {}, 128 | "outputs": [], 129 | "source": [ 130 | "flash_speed = 0.25" 131 | ] 132 | }, 133 | { 134 | "cell_type": "markdown", 135 | "metadata": {}, 136 | "source": [ 137 | "Then, call `flash_pixels` and pass in the `flash_speed` variable we just created" 138 | ] 139 | }, 140 | { 141 | "cell_type": "code", 142 | "execution_count": null, 143 | "metadata": {}, 144 | "outputs": [], 145 | "source": [ 146 | "flash_pixels(flash_speed)" 147 | ] 148 | }, 149 | { 150 | "cell_type": "markdown", 151 | "metadata": {}, 152 | "source": [ 153 | "Want more? Read the full tutorial [on the Adafruit Learning System](https://learn.adafruit.com/adafruit-circuit-playground-express/circuitpython-neopixel)" 154 | ] 155 | } 156 | ], 157 | "metadata": { 158 | "kernelspec": { 159 | "display_name": "CircuitPython", 160 | "language": "python", 161 | "name": "circuitpython" 162 | }, 163 | "language_info": { 164 | "codemirror_mode": { 165 | "name": "python", 166 | "version": 3 167 | }, 168 | "file_extension": ".py", 169 | "mimetype": "text/x-python", 170 | "name": "python", 171 | "pygments_lexer": "python3", 172 | "version": "3" 173 | } 174 | }, 175 | "nbformat": 4, 176 | "nbformat_minor": 2 177 | } 178 | -------------------------------------------------------------------------------- /examples/Using_magics.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "id": "ordered-violence", 6 | "metadata": {}, 7 | "source": [ 8 | "# Tutorial using the inbuilt magics %python and %%python" 9 | ] 10 | }, 11 | { 12 | "cell_type": "markdown", 13 | "id": "micro-palestine", 14 | "metadata": {}, 15 | "source": [ 16 | "Ipython magics allow many things to be done inside jupyter notebooks from running bash scripts to running interactive plotting tools.\n", 17 | "This tutorial looks at implementing simple magics command which allows the user to run Python code in the notebook whilst also running circuit python.\n", 18 | "The 2 magics that are available to the user are `%python` and `%%python`:\\\n", 19 | "\n", 20 | "`%python` : is a line magic allowing a single line of python code to be run following the `%python` keyword\\\n", 21 | "`%%python` : is a cell magic allowing the user to run the whole cell in python, rather than circuitpython.\\\n", 22 | "\n", 23 | "\n", 24 | "Below is a quick example of this in play." 25 | ] 26 | }, 27 | { 28 | "cell_type": "markdown", 29 | "id": "smooth-wesley", 30 | "metadata": {}, 31 | "source": [ 32 | "To begin with we can make sure we are using the circuit python kernel by checking the top left corner of the page." 33 | ] 34 | }, 35 | { 36 | "cell_type": "markdown", 37 | "id": "studied-knowing", 38 | "metadata": {}, 39 | "source": [ 40 | "To show that the system is connected to the circuitpython we run the below command." 41 | ] 42 | }, 43 | { 44 | "cell_type": "code", 45 | "execution_count": null, 46 | "id": "mineral-herald", 47 | "metadata": {}, 48 | "outputs": [], 49 | "source": [ 50 | "import os\n", 51 | "print(os.uname())" 52 | ] 53 | }, 54 | { 55 | "cell_type": "markdown", 56 | "id": "prepared-object", 57 | "metadata": {}, 58 | "source": [ 59 | "We can see from above the system is connected to the microcontroller." 60 | ] 61 | }, 62 | { 63 | "cell_type": "markdown", 64 | "id": "remarkable-change", 65 | "metadata": {}, 66 | "source": [ 67 | "Following this we can run a python line magic to import a single library. " 68 | ] 69 | }, 70 | { 71 | "cell_type": "code", 72 | "execution_count": 2, 73 | "id": "worse-pipeline", 74 | "metadata": {}, 75 | "outputs": [], 76 | "source": [ 77 | "%python import numpy" 78 | ] 79 | }, 80 | { 81 | "cell_type": "markdown", 82 | "id": "radical-breath", 83 | "metadata": {}, 84 | "source": [ 85 | "This line magics still works when we string code together with semicolons. Which we can see in the following cell." 86 | ] 87 | }, 88 | { 89 | "cell_type": "code", 90 | "execution_count": 4, 91 | "id": "current-creativity", 92 | "metadata": {}, 93 | "outputs": [], 94 | "source": [ 95 | "%python import numpy as np; a = 2; b = 4; np.sum((a,b))" 96 | ] 97 | }, 98 | { 99 | "cell_type": "markdown", 100 | "id": "sixth-commitment", 101 | "metadata": {}, 102 | "source": [ 103 | "We can also run cell magics by adding the `%%python` which we see in the cell below." 104 | ] 105 | }, 106 | { 107 | "cell_type": "code", 108 | "execution_count": 9, 109 | "id": "divine-spread", 110 | "metadata": {}, 111 | "outputs": [], 112 | "source": [ 113 | "%%python\n", 114 | "import nbdev\n", 115 | "import numpy as np\n", 116 | "a = 2\n", 117 | "b = 4\n", 118 | "np.sum((a,b))" 119 | ] 120 | }, 121 | { 122 | "cell_type": "code", 123 | "execution_count": null, 124 | "id": "compliant-syracuse", 125 | "metadata": {}, 126 | "outputs": [], 127 | "source": [] 128 | }, 129 | { 130 | "cell_type": "code", 131 | "execution_count": null, 132 | "id": "demanding-dover", 133 | "metadata": {}, 134 | "outputs": [], 135 | "source": [] 136 | } 137 | ], 138 | "metadata": { 139 | "kernelspec": { 140 | "display_name": "CircuitPython", 141 | "language": "python", 142 | "name": "circuitpython" 143 | }, 144 | "language_info": { 145 | "codemirror_mode": { 146 | "name": "python", 147 | "version": 3 148 | }, 149 | "file_extension": ".py", 150 | "mimetype": "text/x-python", 151 | "name": "python", 152 | "pygments_lexer": "python3", 153 | "version": "3" 154 | } 155 | }, 156 | "nbformat": 4, 157 | "nbformat_minor": 5 158 | } 159 | -------------------------------------------------------------------------------- /examples/using_magics_and_nbdev.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "id": "requested-binding", 6 | "metadata": {}, 7 | "source": [ 8 | "# Creating a workflow for jupyter notebook with nbdev" 9 | ] 10 | }, 11 | { 12 | "cell_type": "markdown", 13 | "id": "musical-label", 14 | "metadata": {}, 15 | "source": [ 16 | "## Getting started" 17 | ] 18 | }, 19 | { 20 | "cell_type": "markdown", 21 | "id": "subtle-franklin", 22 | "metadata": {}, 23 | "source": [ 24 | "To begin with a note to all users, this tutorial is meant to be implemented in your own jupyter notebook following the steps provided here. Without undertaking the steps in your own environment the script wont work." 25 | ] 26 | }, 27 | { 28 | "cell_type": "markdown", 29 | "id": "explicit-corrections", 30 | "metadata": {}, 31 | "source": [ 32 | "To develop a workflow that allows us to run REPL based circuit python and then create a code.py file as well as any other assorted library, we utilise an excellent package known as nbdev. Developed by the team at fastai. To get this package first install nbdev from pip." 33 | ] 34 | }, 35 | { 36 | "cell_type": "code", 37 | "execution_count": null, 38 | "id": "sitting-enhancement", 39 | "metadata": {}, 40 | "outputs": [], 41 | "source": [ 42 | "!pip install nbdev" 43 | ] 44 | }, 45 | { 46 | "cell_type": "markdown", 47 | "id": "running-infrared", 48 | "metadata": {}, 49 | "source": [ 50 | "## Create repository\n", 51 | "Once installed the next step is to create a new repo from the nbdev template https://github.com/fastai/nbdev_template/generate. (You need to be logged in to github). Fill in the requested info and click Create repository from template.\n", 52 | "\n", 53 | "## Edit settings.ini\n", 54 | "\n", 55 | "You will need to add some information to the settings.ini file to get the system working. Each setting can be changed as needed for your project\n", 56 | "\n", 57 | "```\n", 58 | "# lib_name = your_project_name\n", 59 | "# repo_name = name of github repo\n", 60 | "# user = your_github_username\n", 61 | "# description = A description of your project\n", 62 | "# keywords = some keywords\n", 63 | "# author = Your Name\n", 64 | "# author_email = email@example.com\n", 65 | "# copyright = Your Name or Company Name\n", 66 | "# branch = The default branch of your GitHub repo (usually either master or main)\n", 67 | "```" 68 | ] 69 | }, 70 | { 71 | "cell_type": "markdown", 72 | "id": "senior-ownership", 73 | "metadata": {}, 74 | "source": [ 75 | "## Build your first notebook" 76 | ] 77 | }, 78 | { 79 | "cell_type": "markdown", 80 | "id": "minimal-palestinian", 81 | "metadata": {}, 82 | "source": [ 83 | "Once done we can run jupyter notebook/lab. and open 00_core.ipynb. You will notice at the top the notebook says\n", 84 | "`#default_exp core` Change this to `#default_exp code`, this creates a name for your main py file for the circuit python." 85 | ] 86 | }, 87 | { 88 | "cell_type": "markdown", 89 | "id": "atmospheric-disclaimer", 90 | "metadata": {}, 91 | "source": [ 92 | "Inside the notebook we want to change the kernel to CircuitPython by going to the Kernel tab and changing to the circuitpython kernel. Once done we can begin coding our circuit python library." 93 | ] 94 | }, 95 | { 96 | "cell_type": "markdown", 97 | "id": "pending-christian", 98 | "metadata": {}, 99 | "source": [ 100 | "To begin with we can place this python cell magic below the `default_exp` line to allow auto scripting of the notebook at any stage." 101 | ] 102 | }, 103 | { 104 | "cell_type": "code", 105 | "execution_count": null, 106 | "id": "standing-alliance", 107 | "metadata": {}, 108 | "outputs": [], 109 | "source": [ 110 | "%%python\n", 111 | "from nbdev.export import notebook2script; notebook2script()" 112 | ] 113 | }, 114 | { 115 | "cell_type": "markdown", 116 | "id": "gentle-syria", 117 | "metadata": {}, 118 | "source": [ 119 | "The `%%python` magic runs everything in the cell. In this situation we could also run `%python from nbdev.export import notebook2script; notebook2script()` and it would still run the same." 120 | ] 121 | }, 122 | { 123 | "cell_type": "markdown", 124 | "id": "discrete-afghanistan", 125 | "metadata": {}, 126 | "source": [ 127 | "After this we are free to run whatever code we wish. To create exportable function or class blocks we add the `#export` keyword to the top of the cell. This tells nbdev.export which functions to search for in the notebook. \n", 128 | "\n", 129 | "The below code is for a simple blink script for the raspberrypi pico" 130 | ] 131 | }, 132 | { 133 | "cell_type": "code", 134 | "execution_count": null, 135 | "id": "entitled-protection", 136 | "metadata": {}, 137 | "outputs": [], 138 | "source": [ 139 | "#export\n", 140 | "import time\n", 141 | "import os\n", 142 | "import board\n", 143 | "import digitalio\n", 144 | "led = digitalio.DigitalInOut(board.LED)\n", 145 | "led.direction = digitalio.Direction.OUTPUT\n", 146 | "for i in range(4):\n", 147 | " led.value = True\n", 148 | " time.sleep(0.5)\n", 149 | " led.value = False\n", 150 | " time.sleep(0.5)\n", 151 | "\n" 152 | ] 153 | }, 154 | { 155 | "cell_type": "markdown", 156 | "id": "tough-wayne", 157 | "metadata": {}, 158 | "source": [ 159 | "There are many more things we can begin to do with this development pipeline. But for now we are able to play with the repl environment and save our code to a code.py file. One final thing to note as of now we still have to manually move our file to the circuitpython microcontroller." 160 | ] 161 | } 162 | ], 163 | "metadata": { 164 | "kernelspec": { 165 | "display_name": "CircuitPython", 166 | "language": "python", 167 | "name": "circuitpython" 168 | }, 169 | "language_info": { 170 | "codemirror_mode": { 171 | "name": "python", 172 | "version": 3 173 | }, 174 | "file_extension": ".py", 175 | "mimetype": "text/x-python", 176 | "name": "python", 177 | "pygments_lexer": "python3", 178 | "version": "3" 179 | } 180 | }, 181 | "nbformat": 4, 182 | "nbformat_minor": 5 183 | } 184 | -------------------------------------------------------------------------------- /readthedocs.yml: -------------------------------------------------------------------------------- 1 | name: circuitpython_kernel 2 | type: sphinx 3 | conda: 4 | file: docs/environment.yml 5 | python: 6 | version: 3 7 | -------------------------------------------------------------------------------- /requirements_dev.txt: -------------------------------------------------------------------------------- 1 | pip==10.0.1 2 | bumpversion==0.5.3 3 | wheel==0.31.1 4 | watchdog==0.8.3 5 | flake8==3.5.0 6 | coverage==4.5.1 7 | sphinx==1.7.5 8 | cryptography==2.2.2 9 | PyYAML==3.12 10 | Flask-Sphinx-Themes==1.0.2 11 | recommonmark==0.4.0 12 | pylint==1.9.2 13 | -------------------------------------------------------------------------------- /setup.cfg: -------------------------------------------------------------------------------- 1 | [bumpversion] 2 | current_version = 0.3.2 3 | commit = True 4 | tag = True 5 | 6 | [bumpversion:file:setup.py] 7 | search = version='{current_version}' 8 | replace = version='{new_version}' 9 | 10 | [bumpversion:file:circuitpython_kernel/__init__.py] 11 | search = __version__ = '{current_version}' 12 | replace = __version__ = '{new_version}' 13 | 14 | [bdist_wheel] 15 | universal = 1 16 | 17 | [flake8] 18 | exclude = docs 19 | -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | 4 | from setuptools import setup 5 | 6 | with open('README.rst') as readme_file: 7 | readme = readme_file.read() 8 | 9 | with open('HISTORY.rst') as history_file: 10 | history = history_file.read() 11 | 12 | requirements = ['pyserial', 'ipython', 'ipykernel', 'pylint'] 13 | 14 | 15 | test_requirements = [ 16 | # TODO: put package test requirements here 17 | ] 18 | 19 | 20 | setup( 21 | name='circuitpython_kernel', 22 | version='0.3.2', 23 | description="CircuitPython Kernel enables CircuitPython to be used in Jupyter Notebooks for learning Python coding with microcontrollers.", 24 | long_description=readme + '\n\n' + history, 25 | author="Carol Willing", 26 | author_email='carolcode@willingconsulting.com', 27 | url='https://github.com/adafruit/circuitpython_kernel', 28 | packages=['circuitpython_kernel'], 29 | package_dir={'circuitpython_kernel': 'circuitpython_kernel'}, 30 | entry_points={}, 31 | include_package_data=True, 32 | install_requires=requirements, 33 | license="BSD license", 34 | zip_safe=False, 35 | keywords='circuitpython_kernel kernel jupyter notebook', 36 | classifiers=[ 37 | 'Development Status :: 2 - Pre-Alpha', 38 | 'Intended Audience :: Developers', 39 | 'License :: OSI Approved :: BSD License', 40 | 'Natural Language :: English', 41 | 'Programming Language :: Python :: 3.6', 42 | ], 43 | test_suite='tests', 44 | tests_require=test_requirements, 45 | ) 46 | -------------------------------------------------------------------------------- /tests/__init__.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | -------------------------------------------------------------------------------- /tests/test_circuitpython_kernel.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | 4 | """ 5 | test_circuitpython_kernel 6 | ---------------------------------- 7 | 8 | Tests for `circuitpython_kernel` module. 9 | """ 10 | 11 | 12 | import sys 13 | import unittest 14 | from contextlib import contextmanager 15 | 16 | from circuitpython_kernel import kernel 17 | 18 | 19 | 20 | class TestCircuitpython_kernel(unittest.TestCase): 21 | 22 | def setUp(self): 23 | pass 24 | 25 | def tearDown(self): 26 | pass 27 | 28 | def test_000_something(self): 29 | pass 30 | 31 | def test_command_line_interface(self): 32 | pass 33 | -------------------------------------------------------------------------------- /travis_pypi_setup.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | """Update encrypted deploy password in Travis config file 4 | """ 5 | 6 | 7 | from __future__ import print_function 8 | import base64 9 | import json 10 | import os 11 | from getpass import getpass 12 | import yaml 13 | from cryptography.hazmat.primitives.serialization import load_pem_public_key 14 | from cryptography.hazmat.backends import default_backend 15 | from cryptography.hazmat.primitives.asymmetric.padding import PKCS1v15 16 | 17 | 18 | try: 19 | from urllib import urlopen 20 | except: 21 | from urllib.request import urlopen 22 | 23 | 24 | GITHUB_REPO = 'willingc/circuitpython_kernel' 25 | TRAVIS_CONFIG_FILE = os.path.join( 26 | os.path.dirname(os.path.abspath(__file__)), '.travis.yml') 27 | 28 | 29 | def load_key(pubkey): 30 | """Load public RSA key, with work-around for keys using 31 | incorrect header/footer format. 32 | 33 | Read more about RSA encryption with cryptography: 34 | https://cryptography.io/latest/hazmat/primitives/asymmetric/rsa/ 35 | """ 36 | try: 37 | return load_pem_public_key(pubkey.encode(), default_backend()) 38 | except ValueError: 39 | # workaround for https://github.com/travis-ci/travis-api/issues/196 40 | pubkey = pubkey.replace('BEGIN RSA', 'BEGIN').replace('END RSA', 'END') 41 | return load_pem_public_key(pubkey.encode(), default_backend()) 42 | 43 | 44 | def encrypt(pubkey, password): 45 | """Encrypt password using given RSA public key and encode it with base64. 46 | 47 | The encrypted password can only be decrypted by someone with the 48 | private key (in this case, only Travis). 49 | """ 50 | key = load_key(pubkey) 51 | encrypted_password = key.encrypt(password, PKCS1v15()) 52 | return base64.b64encode(encrypted_password) 53 | 54 | 55 | def fetch_public_key(repo): 56 | """Download RSA public key Travis will use for this repo. 57 | 58 | Travis API docs: http://docs.travis-ci.com/api/#repository-keys 59 | """ 60 | keyurl = 'https://api.travis-ci.org/repos/{0}/key'.format(repo) 61 | data = json.loads(urlopen(keyurl).read().decode()) 62 | if 'key' not in data: 63 | errmsg = "Could not find public key for repo: {}.\n".format(repo) 64 | errmsg += "Have you already added your GitHub repo to Travis?" 65 | raise ValueError(errmsg) 66 | return data['key'] 67 | 68 | 69 | def prepend_line(filepath, line): 70 | """Rewrite a file adding a line to its beginning. 71 | """ 72 | with open(filepath) as f: 73 | lines = f.readlines() 74 | 75 | lines.insert(0, line) 76 | 77 | with open(filepath, 'w') as f: 78 | f.writelines(lines) 79 | 80 | 81 | def load_yaml_config(filepath): 82 | with open(filepath) as f: 83 | return yaml.load(f) 84 | 85 | 86 | def save_yaml_config(filepath, config): 87 | with open(filepath, 'w') as f: 88 | yaml.dump(config, f, default_flow_style=False) 89 | 90 | 91 | def update_travis_deploy_password(encrypted_password): 92 | """Update the deploy section of the .travis.yml file 93 | to use the given encrypted password. 94 | """ 95 | config = load_yaml_config(TRAVIS_CONFIG_FILE) 96 | 97 | config['deploy']['password'] = dict(secure=encrypted_password) 98 | 99 | save_yaml_config(TRAVIS_CONFIG_FILE, config) 100 | 101 | line = ('# This file was autogenerated and will overwrite' 102 | ' each time you run travis_pypi_setup.py\n') 103 | prepend_line(TRAVIS_CONFIG_FILE, line) 104 | 105 | 106 | def main(args): 107 | public_key = fetch_public_key(args.repo) 108 | password = args.password or getpass('PyPI password: ') 109 | update_travis_deploy_password(encrypt(public_key, password.encode())) 110 | print("Wrote encrypted password to .travis.yml -- you're ready to deploy") 111 | 112 | 113 | if '__main__' == __name__: 114 | import argparse 115 | parser = argparse.ArgumentParser(description=__doc__) 116 | parser.add_argument('--repo', default=GITHUB_REPO, 117 | help='GitHub repo (default: %s)' % GITHUB_REPO) 118 | parser.add_argument('--password', 119 | help='PyPI password (will prompt if not provided)') 120 | 121 | args = parser.parse_args() 122 | main(args) 123 | --------------------------------------------------------------------------------