# Creating and sharing a CircuitPython library

## Overview

Open source software is awesome. It's a shared resource that we all work on to benefit ourselves and others all at once. CircuitPython is built on the open source code of MicroPython for example.

One challenge of open source software is making your code and documentation available in a way that makes it accessible to those that use it. It's not just a matter of writing code and posting it somewhere on the internet. For the core of CircuitPython we have existing source control on [GitHub](https://github.com/adafruit/circuitpython), low-level documentation on [ReadTheDocs](http://circuitpython.readthedocs.io/en/latest/), continuous testing on [GitHub Actions](https://github.com/features/actions), tutorials on [Adafruit Learn System](https://learn.adafruit.com/search?q=circuitpython&amp;), community support on the [Adafruit Forums](https://forums.adafruit.com/viewforum.php?f=60) and live chat on [Discord](https://adafru.it/discord).

However, there is much more to CircuitPython than just the core code. There are also additional libraries that expand what can be done on CircuitPython. They may add additional support for a particular FeatherWing or making simple tasks easier.

To make CircuitPython libraries easy to find and understand we've come up with a number of best practices when creating a new library. This guide is an overview of those practices with links to more information on specific topics.

# Creating and sharing a CircuitPython library

## Creating a library

## Getting coding ##
Usually when creating a library to share with the world, the biggest issue isn't the code itself. Its common for a library to evolve naturally out of an existing project. It may have been a clock project where you switched the real-time clock partway through or a simple project which has grown in complexity demanding some code refactoring. The first challenge is identifying what can make up a library.

In CircuitPython (and Python in general) libraries are known as packages and modules. A module is a single python file that can be imported by another file by using the `import` statement. A package is a folder filled with modules and is used to group common modules together. The [`adafruit_rgb_display`](https://github.com/adafruit/Adafruit_CircuitPython_RGB_Display) package is an example of this. Both packages and modules can be called libraries.

By `import`ing libraries, code can gain functionality without the clutter of additional code within the same file. Instead, one only needs to understand the [Application Programming Interface](https://en.wikipedia.org/wiki/Application_programming_interface) or API to use the power of the imported library. Its not important to read and understand the code that implements the API. Outside of software, you can think of the steering wheel in a car as the API to the turning functionality of the car. You don't need to understand how the car's wheels turn, you just know that when you turn the steering wheel, the car turns.

Hopefully, as your project has evolved, you've begun to use functions and classes to create APIs. Creating powerful, easy to understand APIs is a difficult thing to do and something a Google search can provide many resources on. Here is one Python specific talk from PyCon 2017: https://www.youtube.com/watch?v=4mkFfce46zE

Once you've got an API you are happy with, its time to make it available to the world.

## Cookie cutter ##
One of the first helpful tools for creating a CircuitPython library is called [CookieCutter](https://cookiecutter.readthedocs.io/en/latest/). It is an open source project that was made to help manage common content between projects. This common content is also called a boiler plate. For CircuitPython libraries, this boiler plate can include more than twenty files. Whoa! Thats a lot for a simple library. However, they are all useful. Only one will hold your code. Below is an overview. Don't worry too much about understanding it all because most relate to topics we'll discuss later.

* `.gitignore` - Prevents autogenerated files from being shared publicly by git.
* `.github/workflows/` - A set of workflows run by GitHub Actions when something is committed, a PR is opened or updated, or the library is released.
* `CODE_OF_CONDUCT.md` - Outlines expectations for behavior of contributors to ensure a friendly, welcoming environment. Source from [Contributor Convenant](http://contributor-covenant.org/).
* `{something}.license` - These contain license information for the file specified by the name. e.g. examples.rst.license is the license for examples.rst.
* `LICENSE` - Contains the legal text for the license which allows others to use and modify the code you've written. We default to [MIT](https://choosealicense.com/licenses/mit/) but others can be used. We recommend [choosealicense.com](https://choosealicense.com/) by GitHub for an easy way to decide what is right for you. Typically a comment will be placed at the top of source code files to clarify their license if it gets copied away from the rest of the files.
* `README.rst` - This is your most important documentation! It is the first thing people read when they unzip your source or pull it up on GitHub. It can be [plain text](https://en.wikipedia.org/wiki/Plain_text), [Markdown](https://daringfireball.net/projects/markdown/syntax) (.md) or [reStructuredText](http://docutils.sourceforge.net/rst.html) (.rST). The latter two will be nicely rendered on GitHub. We prefer .rST for CircuitPython libraries because it makes it easy to integrate with [Sphinx](http://www.sphinx-doc.org/en/stable/), [ReadTheDocs](https://readthedocs.org/) and [autogenerated documentation](http://www.sphinx-doc.org/en/stable/ext/autodoc.html).
* `adafruit_{name}.py` - Your code! This is the module you can use to organize your code. It automatically includes example license text and comments. It should start with a different name for Community Bundle projects.
* `docs/api.rst` - Triggers the autogeneration of documentation for the above module.
* `docs/conf.py` - Configuration settings for [Sphinx](http://www.sphinx-doc.org/en/stable/).
* `docs/examples.rst` - Examples section, gets inclueded in documentation pages.
* `docs/index.rst` - Table of contents and main docs page section.
* `readthedocs.yml` - Configuration settings for [ReadTheDocs](https://readthedocs.org/)
* `requirements.txt` - Lists other libraries that your depends on to work correctly. Typically things you `import` in your code that aren't provided by CircuitPython automatically.
* `.pre-commit-config.yaml` - Configuration settings for the pre-commit tool. This sets the versions for black and reuse.
* `.pylintrc` - Configuration settings for [Pylint](https://www.pylint.org/).
* `setup.py` - Required for PyPI deployment. File will be present even if library is not deployed to PyPI.
* `examples/{name}_simpletest.py` - You should put a basic example script in this file, to show how the library is used.

If your making a library for the Adafruit Bundle the filename of your library is `adafruit_{name}.py`. The `{name}` portion will be replaced when the cookiecutter is actually run. For Community Bundle projects you'll enter different values for some prompts so your code file will come out with a different name. Only libraries supported by Adafruit should keep the `adafruit_` prefix.

Primary: 

## Installing cookiecutter (Mac & Linux)

cookiecutter is itself a Python library so you'll need Python and `pip` installed first. Then run:

```terminal
pip install cookiecutter
```

## Running cookiecutter (Mac & Linux)

Running cookiecutter is equally easy. All it needs is a location of where to pull the template from. Everything else is done via prompt.

```terminal
cookiecutter gh:adafruit/cookiecutter-adafruit-circuitpython
```

## Installing cookiecutter (Windows)

At the moment, cookiecutter is incompatible with Windows systems. You'll need to install an alternate version of cookiecutter:

```terminal
pip install ansys-cookiecutter
```

## Running cookiecutter (Windows)

Running cookiecutter is equally easy. All it needs is a location of where to pull the template from. Everything else is done via prompt.

```terminal
cookiecutter https://github.com/jepler/cookiecutter-adafruit-circuitpython/archive/refs/heads/ansys-cookiecutter.zip
```

# Prompts

- **target\_bundle&nbsp;** - Which bundle is the library for? 1 (default) for Adafruit Bundle. 2 for the Community Bundle.
- **github\_user** - GitHub user or organization which will host this repo. For example, Adafruit funded libraries should say `adafruit` here.
- **author\_name** - Who you are! Sets the copyright to you.
- **company** - Used to give Copyright credit to the company funding the library. For example, Adafruit funded libraries should say "Adafruit Industries" here.
- **library\_name** - Shortest name for the library. Usually a chip name such as `LIS3DH`. THIS MUST BE ENTERED EXACTLY THE SAME WAY IT WILL LOOK IN THE GITHUB REPO URL (e.g. for github.com/adafruit/Adafruit\_CircuitPython\_CharLCD, you must enter `CharLCD`). Use all caps or camel case as necessary. If you enter this differently than the GitHub URL, you will need to fix a number of things in your library later.
- **library\_description** - Write a sentence describing the purpose of this library (e.g. `CircuitPython helper library for the DC & Stepper Motor FeatherWing, Shield and Pi Hat kits.`).
- **library\_keywords** - Used to populate keywords for searching for the library on PyPi. Enter a string of lowercase keywords (e.g `dht temp humidity`) Please be thorough! The more search keywords you enter, the easier it is to find. This step should be completed even if you don't think the library will end up deployed on PyPI. NOTE: The following are included by default: `adafruit`, `blinka`, `circuitpython`, `micropython`, and `library_name`.
- **library\_prefix** - Used to prefix the code to the organization creating the library. For example, Adafruit supported libraries should say `Adafruit` here. Do not add a `-` or `_`.
- **adafruit\_pid** - Numeric Adafruit product ID associated with this library. Leave blank if none.
- **requires\_bus\_device** - Determines whether to add comments about a dependency on [https://github.com/adafruit/Adafruit\_CircuitPython\_BusDevice](https://github.com/adafruit/Adafruit_CircuitPython_BusDevice). Leave empty if the library won't use BusDevice.
- **requires\_register** - Determines whether to add comments about a dependency on [https://github.com/adafruit/Adafruit\_CircuitPython\_Register](https://github.com/adafruit/Adafruit_CircuitPython_Register). Leave empty if the library won't use Register.
- **other\_requirements** - Add any other module dependencies. Enter a comma separated string of modules, using the lowercase full name of the module, using `-` instead of `_`&nbsp; (e.g. `adafruit-circuitpython-pca9685, adafruit-circuitpython-motor, pyserial`). This is used to for PyPI. This step should be completed even if you don't think the library will be deployed to PyPI.. NOTE: `Adafruit-Blinka` is always included, so no need to include it here.
- **pypi\_release** - Will the library be released on PyPi? y/n.
- **sphinx\_docs** - Will the library have sphinx documentation files? y/n.
- **default\_branch** - Should the repo use master or main as the default branch name.

![](https://cdn-learn.adafruit.com/assets/assets/000/099/412/medium800/circuitpython_cookie_cutter_run_narrower.png?1613182744)

![](https://cdn-learn.adafruit.com/assets/assets/000/099/411/medium800/circuitpython_cookie_cutter_result.png?1613182588)

At this point all the files you need should be in place. Now you'll need to migrate your code into the generated `.py` file. If you have a lot of code you can make a directory of the same name and have multiple modules within it. For now, we'll keep it simply a module.

## MPY

Once your code is there, test it in CircuitPython by copying it over to your board. If you get a MemoryError on import you can create a smaller **.mpy** file that's easier to load in CircuitPython.

Executables for **mpy-cross** for Windows, MacOS, Linux x64, and Raspbian are available at the link below. The 6.x versions will also work for 5.x. The **.mpy** format changed in CircuitPython 7, so check the version number in the **mpy-cross** filename.

[mpy-cross versions for CircuitPython 6.x, 7.x, and later](https://adafruit-circuit-python.s3.amazonaws.com/index.html?prefix=bin/mpy-cross/)
Alternatively you can build&nbsp;`mpy-cross`&nbsp;yourself. You'll need to download (or clone) the CircuitPython source, make&nbsp;`mpy-cross`&nbsp;and run it on your source file. See the [Building CircuitPython guide](https://learn.adafruit.com/building-circuitpython) for detailed setup instructions.

```terminal
git clone https://github.com/adafruit/circuitpython.git
cd circuitpython
make fetch-all-submodules
cd mpy-cross
make
./mpy-cross example/adafruit_example.py
```

Make sure&nbsp;`example/adafruit_example.py`&nbsp;is changed to your file. It will make a&nbsp;`example/adafruit_example.mpy`&nbsp;file that you can copy to CircuitPython just like a&nbsp;`.py`&nbsp;file.

 **CircuitPython 3.x and 4.x:** This zip contains mpy-cross binaries for MacOS X, Raspbian, Ubuntu x86 and Windows that will work with **old versions of** CircuitPython 3.x and 4.x:

[mpy-cross-3.x-and-4.x.zip](https://cdn-learn.adafruit.com/assets/assets/000/077/960/original/mpy-cross.zip?1562633743)
[If you need mpycross for CircuitPython 2.x, check out the assets attached to this CircuitPython release](https://github.com/adafruit/circuitpython/releases/tag/2.3.0)

## Saving your work ##
At this point you've done a ton of work getting your library code going, initally tested and settings in place. Its time to commit your code and prep to publish this first version to the world.

Committing code is a way to keep track of how code changes over time. It also makes it possible to view what the files looked like previously. The simplest way to do this is to copy the whole folder to a new location. However, source control software is a much better way to organize it. Source control software makes it easy to view changes between versions (called diffs), back up the code to another computer and collaborate with others.

git is our and many others' preferred source control software. It was created by the same person as the Linux kernel and quickly grew in popularity due to its use there and its distributed nature. [GitHub](https://github.com/), a website for project hosting, grew up around git and is now the de facto place on the web to share your projects. Open source projects are hosted for free and private projects are hosted for a small fee.

Anyways, lets get `git` going to commit our code locally. See [this guide](https://learn.adafruit.com/an-introduction-to-collaborating-with-version-control) for details on installing `git`. Consider this a quick start to initializing a repository (make sure you are in the library's directory).

![](https://cdn-learn.adafruit.com/assets/assets/000/043/347/medium800/micropython_init.png?1498863485)

The first command initializes a `.git` folder that stores the repository metadata. The second stages all of the files in the directory to be committed. Being more precise with individual files is recommended when changing your code for multiple reasons at once. Making two commits will make it easier to understand what changed and why. For now, we'll do one initial commit.

The final step will actually commit your code after prompting your for a commit message. In the message you should give a short one line summary followed by a detailed paragraph about the changes. The video below gives good tips on commit messages and code reviews.

https://www.youtube.com/watch?v=iNG1a--SIlk

Info: 

# Creating and sharing a CircuitPython library

## Sharing on GitHub

Now that we've got everything squared away on our own computer, its time to share it with the world. The main avenue we'll use to share the code is GitHub, a code hosting site that is very popular with open source projects and developers. Once that is setup, we'll set up ReadTheDocs integration so that our documentation is hosted and automatically updated. We'll set up GitHub Actions so that our code is automatically checked by mpy-cross and that releases have mpy files on them.

## GitHub ##
[GitHub](https://github.com/) is the bees knees. They offer free project hosting including code, issues, wiki and website for open source projects. It is extremely popular for good reason. They integrate with git to host your code.

So, the first step to sharing your project is [creating the GitHub git repository](https://github.com/new).

![](https://cdn-learn.adafruit.com/assets/assets/000/043/346/medium800/micropython_new_repo.png?1498863405)

The repository name can be anything but we're sticking to `Adafruit_CircuitPython_{name}` for official Adafruit libraries and suggest `CircuitPython_{name}` for community libraries. Fill in the description with a short, informative sentence about your library. **DO NOT** check the Initialize box. We'll do that with our existing files in the next step.

![](https://cdn-learn.adafruit.com/assets/assets/000/043/348/medium800/micropython_created_repo.png?1498863894)

On the next page click **SSH** and copy the text to the right of it starting with `git@`. We're going to follow the second set of instructions because we've already created our local git repository (repo for short). The first step makes our local repo aware of the GitHub repo and names it `tannewt` rather than `origin` because I find it clearer to match the GitHub repository name. The second step pushes the commit we made before to the GitHub repository and makes the code public.

```bash
git remote add  
git push -u origin main
```

Info: 

![](https://cdn-learn.adafruit.com/assets/assets/000/043/349/medium800/micropython_first_push.png?1498864298)

At this point, you may have been prompted for your GitHub password. If you entered it correctly it should work. I use [GitHub Desktop](https://desktop.github.com/) to manage my login and recommend it.

Now we can verify our code is live by refresh our browser tab.

![](https://cdn-learn.adafruit.com/assets/assets/000/043/350/medium800/micropython_were_live.png?1498864570)

**Woohoo!** We're live! While we're here. Lets add the `circuitpython` topic to our repo by clicking `Add topics`, typing in `circuitpython` and clicking `Done`.

![](https://cdn-learn.adafruit.com/assets/assets/000/043/351/medium800/micropython_topic.png?1498864639)

## Better README ##
Now if we scroll down, we'll see our README. This is the single most important document for the whole project. It should be concise but contain basic description of the library, what it depends on, how to use it, how to contribute and where to find more detailed documentation.

![](https://cdn-learn.adafruit.com/assets/assets/000/043/352/medium800/micropython_first_readme.png?1498864933)

Since our README doesn't have that information we'll do a quick edit to add some more information. I use [Atom](https://atom.io/) (created by GitHub) for file editing. It has good git integration including highlighting edited lines and files in orange.

![](https://cdn-learn.adafruit.com/assets/assets/000/043/353/medium800/micropython_editing_readme.png?1498865367)

Now that the file is up to date, I do a quick commit and push to make it live.

![](https://cdn-learn.adafruit.com/assets/assets/000/043/354/medium800/micropython_quick_push.png?1498865543)

And if we refresh the repo page we see our new and improved README.

![](https://cdn-learn.adafruit.com/assets/assets/000/043/355/medium800/micropython_new_and_improved.png?1498865737)

Ok! We're up and rolling. The code we've written is public, we've learned how to update it further and have a good README. We can click **Commits** on the front page to see a list of all our public commits on the front page. Next up is getting our docs on [ReadTheDocs](https://readthedocs.org/).

![](https://cdn-learn.adafruit.com/assets/assets/000/043/356/medium800/micropython_commits.png?1498866095)

# Creating and sharing a CircuitPython library

## Sharing docs on ReadTheDocs

Great documentation is a key component of a successful open source library. Luckily, after a little setup, its super easy to write and maintain great docs. 

##Sphinx
First, you should install [Sphinx](http://www.sphinx-doc.org/en/master/). Sphinx allows you to build the docs locally to ensure there aren't any documentation errors when you push to your repository. 








```none
pip install Sphinx sphinx-rtd-theme
```

Now, once you have Sphinx installed:

```none
cd docs
sphinx-build -E -W -b html . _build/html
```

This will output the documentation to `docs/_build/html`. Open the index.html in your browser to view them. It will also (due to `-W`) error out on any warning like GitHub Actions will. This is a good way to locally verify it will pass.

##ReadTheDocs

[ReadTheDocs](https://readthedocs.org/) is another free hosting service used by many open source projects. It integrates with GitHub so that every time new code or docs are pushed to your GitHub, the nicely formatted versions hosted by ReadTheDocs are also updated.


Lets get it setup! First, [sign up and login](https://readthedocs.org/) if you don't already have an account. After signing in, your dashboard will be displayed.

![](https://cdn-learn.adafruit.com/assets/assets/000/043/357/medium800/micropython_readthedocs_dashboard.png?1498866645)

On the dashboard click **Import a Project** and select **Import Manually**.

![](https://cdn-learn.adafruit.com/assets/assets/000/043/358/medium800/micropython_import_manually.png?1498866795)

For the next page we'll need the `https` version of our GitHub repository so we go grab it by clicking **Clone or Download** on the repo front page and selecting **Use HTTPS**.

![](https://cdn-learn.adafruit.com/assets/assets/000/043/359/medium800/micropython_use_https.png?1498866995)

Then press the copy icon.

![](https://cdn-learn.adafruit.com/assets/assets/000/043/360/medium800/micropython_copy_repo_url.png?1498867081)

And paste it into the **Repository URL** field in ReadTheDocs. The name should be **Adafruit CircuitPython Example**. Then click next.

![](https://cdn-learn.adafruit.com/assets/assets/000/043/361/medium800/micropython_rtd_repo_url.png?1498867249)

If you created your ReadTheDocs account and linked your GitHub (like I have below) then it should be able to automatically connect GitHub to itself by creating a Webhook. This Webhook is how ReadTheDocs gets updates from GitHub.

On the right hand side you should also see links to your documentation that you can use to link others to it.

![](https://cdn-learn.adafruit.com/assets/assets/000/043/362/medium800/micropython_webhook_activated.png?1498867714)

Now refresh. We see that its been built successfully on the right and at the top we can view the docs.

![](https://cdn-learn.adafruit.com/assets/assets/000/043/366/medium800/micropython_rtd_built.png?1498868499)

Now, as a last step, lets make sure our docs look as expected by clicking the `View Docs` button.

![](https://cdn-learn.adafruit.com/assets/assets/000/044/246/medium800/circuitpython_rtd_readme.png?1501023318)

Awesome! By default it shows our README.rst (didn't I say it was important?). Furthermore, the lefthand side has a link with our module's name. Lets click it to see the module's documentation.

![](https://cdn-learn.adafruit.com/assets/assets/000/044/247/medium800/circuitpython_rtd_module.png?1501023482)

Yay! Everything looks good. This is exactly as we've defined in our Python file. For now, docs are done. :-)

## Adding Your ReadTheDocs Project as a CircuitPython Subproject

If you're creating documentation for an Adafruit repository, you'll want to add your project as a subproject to the CircuitPython project. Note that you will need access to the CircuitPython project to do this.

Before you do that though, you'll need to add Adabot as a maintainer.&nbsp;

First, click on the&nbsp; **Admin** tab. Then, scroll down and click on the tab labeled&nbsp; **Maintainers** and at the bottom of that tab, enter the name of the user to add,&nbsp; **adabot** in this case, and press&nbsp; **Add**.

![circuitpython_Screenshot_from_2021-08-17_14-47-45.png](https://cdn-learn.adafruit.com/assets/assets/000/103/985/medium640/circuitpython_Screenshot_from_2021-08-17_14-47-45.png?1629226445)

![circuitpython_Screenshot_from_2021-08-17_14-48-11.png](https://cdn-learn.adafruit.com/assets/assets/000/103/986/medium640/circuitpython_Screenshot_from_2021-08-17_14-48-11.png?1629226462)

![circuitpython_Screenshot_from_2021-08-17_14-48-27.png](https://cdn-learn.adafruit.com/assets/assets/000/103/987/medium640/circuitpython_Screenshot_from_2021-08-17_14-48-27.png?1629226487)

Now, navigate over to the CircuitPython project, click on&nbsp; **Admin** , click on&nbsp; **Subprojects,&nbsp;** click&nbsp; **Add Subproject** , and select your project. Set the alias to the project name with&nbsp; **Adafruit CircuitPython** removed.

![circuitpython_Screenshot_from_2021-08-17_14-56-03.png](https://cdn-learn.adafruit.com/assets/assets/000/103/988/medium640/circuitpython_Screenshot_from_2021-08-17_14-56-03.png?1629227083)

![circuitpython_Screenshot_from_2021-08-17_14-56-21.png](https://cdn-learn.adafruit.com/assets/assets/000/103/989/medium640/circuitpython_Screenshot_from_2021-08-17_14-56-21.png?1629227149)

![circuitpython_Screenshot_from_2021-08-17_14-56-35.png](https://cdn-learn.adafruit.com/assets/assets/000/103/990/medium640/circuitpython_Screenshot_from_2021-08-17_14-56-35.png?1629227203)

![circuitpython_Screenshot_from_2021-08-17_15-02-33.png](https://cdn-learn.adafruit.com/assets/assets/000/103/991/medium640/circuitpython_Screenshot_from_2021-08-17_15-02-33.png?1629227258)

# Creating and sharing a CircuitPython library

## Check your code with pre-commit

Maintaining code is quite a challenge over the long-term. Over the lifetime of a library, many people read and edit it. Each person has a different background in coding and also a different goal in mind. These variations can lead to inconsistencies throughout the code that makes it a bit harder for the next person to understand. In CircuitPython libraries, we use tools like Pylint and Black to ensure consistency in new code.

As we've added more automated checks, we've changed to a system called **pre-commit** to manage the checks overall. Once installed properly, you can run pre-commit locally, before committing new code into Git. It also runs remotely on GitHub when a pull request has been proposed. pre-commit is set up to remotely run on all existing libraries. It will automatically run remotely on a new library thanks to cookiecutter. 

However, **it won't run locally unless you install it into your local directory**. We *highly* recommend doing this because it will both check *and* fix your code locally.

## One-time initial install of pre-commit
If you've never used `pre-commit` on your computer before, you'll need to install it **globally** (there is a second "install" for each repository.) The easiest way to install it is with `pip`.

```shell
pip install pre-commit
```

## Workaround for pre-commit issues on Ubuntu 22.04 and Debian
In ubuntu 22.04 or the analogous Debian release, you may see the error _"expected environment for python to be healthy immediately after install"_ when trying to use `pre-commit`. To fix this, add this line to your `.bashrc` or `.bash_aliases` file, or other shell startup file. Restart your shell as necessary to pick up this setting.

```shell
export SETUPTOOLS_USE_DISTUTILS=stdlib
```
This `export` must be present before `pre-commit` sets up its `virtualenv` environment, which happens the first time you do `pre-commit run` or you try to push a commit. If the `virtualenv` is already set up, do `pre-commit clean`, which removes the existing `virtualenv`.

See the instructions from [the pre-commit project for installation](https://pre-commit.com/#installation) for alternative ways of installing `pre-commit`.

Warning: Do not use the `export SETUPTOOLS_USE_DISTUTILS=stdlib` on Ubuntu 24.04 or Debian Bookworm. It will cause some uses of `pip install` to fail.

## Per-repository installation
For **every new repository**, you'll need to perform an pre-commit installation. This installs the specific versions of checks that the repository specifies. From within the repository do:

```shell
pre-commit install
```

After running this command, pre-commit will automatically run when you do `git commit`.

However, if you don't do this, you can still run pre-commit manually.

## Running `pre-commit`
`pre-commit` will run each check every commit for all of the modified files and either pass or fail. Most checks that fail will also modify the source file to make it pass (like removing extra spaces). Once that happens, you'll see newly modified files in `git status`. `git add` them and then try the commit again.

### Manually
You can run the pre-commit checks on every file whenever you like with:

```shell
pre-commit run --all-files
```

## More Info
For more info on `pre-commit` see [pre-commit.com](https://pre-commit.com/).

# Creating and sharing a CircuitPython library

## Run Pylint

Warning: 

Now that you've installed Pylint and downloaded the **.pylintrc** configuration file, you're ready to start linting. First thing we need is an example to check.

Download **pylint\_example.py** using the "pylint\_example.py" link below. Then, place the file in the same location as your recently downloaded **.pylintrc** file.

https://github.com/adafruit/Adafruit_Learning_System_Guides/blob/main/Pylint_and_CircuitPython/pylint_example.py

Pylint [looks in a series of locations](https://docs.pylint.org/en/1.6.0/run.html#command-line-options) in order to find the configuration file. The first place it looks is the current working directory. This is the easiest way to ensure you're using the right configuration file.

Return to your terminal program or command line. From the command line, navigate to the folder containing **pylint\_example.py** and **.pylintrc**. Then, run the following command:

`pylint pylint_example.py`

![](https://cdn-learn.adafruit.com/assets/assets/000/079/741/medium800/circuitpython_pylint_error_tabs_spaces.png?1566422302)

Alright! Your first error! Consider it a badge of honor. And don't worry! The next section will walk through how to read the Pylint output with a series of common errors. Time to start linting!

# Creating and sharing a CircuitPython library

## Black

Warning: 

Black is a really useful code formatting tool maintained by the Python Software Foundation. It reformats files to improve readability.

It can be run in two ways, the first just checks to see if there is any code it would reformat. This is the way we use on all of our CircuitPython repositories. The second way actually reformats the files. This is the way you'll be wanting to use locally.

This page explains how to install and run black, the different ways to run it, and some cases when you may not want to adhere to black's suggestions.

# Installing Black

Installing black is super easy. Simply run:

```none
pip install black
```

Note: if you also have a version of python2 installed, you may have to run:

```none
pip3 install black
```

# Using Black

As I mentioned above, there are two ways to run black. The first way is just checking the code. This is what GitHub actions runs to test commits and pull requests.

This is run by typing in Linux:

```none
black --check --target-version=py35 .
```

And for Windows command line (Python 3 must be installed):

```auto
python -m black .
```

You can replace the `.` with whatever files you want it to check if you don't want it to check every .py file in your current directory.

Here's what that output looks like:

![](https://cdn-learn.adafruit.com/assets/assets/000/090/480/medium800/circuitpython_check.png?1587069253)

However, most of the time, you're going to want Black to actually reformat the code. This is accomplished by running:

```none
black --target-version=py35 .
```

Here's what running that looks like:

![](https://cdn-learn.adafruit.com/assets/assets/000/090/481/medium800/circuitpython_black.png?1587069258)

## Black isn't always right

Sometimes, Black will make a change that just looks really bad. We've mostly encountered this with longer lists of numbers or short strings.&nbsp;

For example: Black would make each element of this list have it's own line.

Here's what the list looked like originally:

```python
heatmap_gp = bytes([
    0, 255, 255, 255, # White
    64, 255, 255, 0,  # Yellow
    128, 255, 0, 0,   # Red
    255, 0, 0, 0])    # Black
```

Here's what the same list looked like after being reformatted by Black:

```python
heatmap_gp = bytes([
	0,
	255,
	255,
	255, # White
	64,
	255,
	255,
	0,  # Yellow
	128,
	255,
	0,
	0,   # Red
	255,
	0,
	0,
	0,
])    # Black
```

You can disable black In that section by adding `# fmt: off` at the start of the section you don't want Black to reformat and `# fmt: on` at the end of said section to re-enable it.

Here's how we disabled Black for the list above:

```python
# fmt: off
heatmap_gp = bytes([
    0, 255, 255, 255, # White
    64, 255, 255, 0,  # Yellow
    128, 255, 0, 0,   # Red
    255, 0, 0, 0])    # Black
# fmt: on
```

## Lint Black's changes

Make sure that after you run Black, you re-run Pylint. They don't always agree, and their major point of disagreement (Pylint's `bad-continuation` check) has been dealt with for all of our CircuitPython repositories.

# Documentation and Contributing

Check out [Black's ReadTheDocs page](https://black.readthedocs.io/en/stable/index.html).

Also, [Black's source code](https://github.com/psf/black) is hosted on a public GitHub repository.

# Creating and sharing a CircuitPython library

## Testing with GitHub Actions

## Navigating GitHub Actions
<font><font><font><font><font><font><font><font><font><font><font><font><font><font><font><font><font><font>To navigate to the actions tab, click on the 'Actions' tab circled near the top of the page.</font></font></font></font></font></font></font></font></font></font></font></font></font></font></font></font></font></font>

![](https://cdn-learn.adafruit.com/assets/assets/000/091/154/medium800/circuitpython_thing.png?1589469140)

![](https://cdn-learn.adafruit.com/assets/assets/000/091/337/medium800/circuitpython_actions_tab.png?1589898425)

1. This is the box that contains all of the Actions workflows that have been run, will be run, or are running for a particular repository. There are four different workflows that Adafruit CircuitPython libraries use. Workflows are a series of tests run to determine if a PR, commit, or release is up to the desired standards and is correctly working. Unless you're releasing libraries, you'll only need to worry about the 'Build CI' workflow.
2. This icon tells you the status of the workflow. A green check means it has run successfully. A red X means it has failed, and an orange circle means it is either waiting to run or currently running.
3. This menu allows you to filter by Workflow. The first three workflows are run upon release, and mostly deal with packaging and deploying the newest version of the library. The fourth one is run when a commit or PR is made and checks formatting and documentation.
4. You can click on the bolded title to the left of number 4 to see the readout from the workflow.

## When your PR fails

As soon as you open a PR, you should see a box with an orange border. It will tell you the status of you PR. While the workflow is running, it will say "Some checks haven't completed yet."

![](https://cdn-learn.adafruit.com/assets/assets/000/091/133/medium800/circuitpython_pic_1.png?1589403317)

<font><font><font><font><font><font>The Actions CI often takes around a minute to run, but depending on the amount of workflows currently being run, and the size of the repository, it could be longer or shorter. Below, you can see that the PR is currently failing actions. To find out what specific test it is failing, you can click on one of four places. First, you can click on the red X to the left of the commit hash. Second, you can click on the red X to the left of the GitHub logo in the orange box. You can also click on details, on the same line as the previous but all the way to the right. Finally, you can click on the 'Actions' tab at the top of the page. This works but it isn't ideal as you'll have to navigate to the test for your PR.</font></font></font></font></font></font>

![](https://cdn-learn.adafruit.com/assets/assets/000/091/151/medium800/circuitpython_pic_2.png?1589467745)

<font><font><font><font><font><font><font><font><font><font><font><font><font><font><font><font><font><font><font><font><font><font><font><font><font><font><font><font><font><font><font><font><font><font><font><font><font><font><font><font><font><font><font><font><font><font><font><font><font><font><font><font><font><font><font><font><font><font><font><font><font><font><font><font><font><font><font><font>Now that you're on the Actions page for that specific commit, you can find out what test is causing it to fail. There are three tests a PR will fail on: black formatting (check formatting), PyLint, and Build Docs. In this case, the Black formatting check is failing.</font></font></font></font></font></font></font></font></font></font></font></font></font></font></font></font></font></font></font></font></font></font></font></font></font></font></font></font></font></font></font></font></font></font></font></font></font></font></font></font></font></font></font></font></font></font></font></font></font></font></font></font></font></font></font></font></font></font></font></font></font></font></font></font></font></font></font></font>

Info: 

![](https://cdn-learn.adafruit.com/assets/assets/000/091/135/medium800/circuitpython_pic_3.png?1589403339)

Fixing a Black formatting failure is actually very easy. Check out the [Black page in this guide](https://learn.adafruit.com/creating-and-sharing-a-circuitpython-library/black) for info on running Black. Then commit and push your changes.

![](https://cdn-learn.adafruit.com/assets/assets/000/091/137/medium800/circuitpython_pic_5.png?1589403374)

Now that we've ran Black, and committed and pushed those changes, we can check back in with the PR. It's still failing, so we can navigate to the Actions page again.

![](https://cdn-learn.adafruit.com/assets/assets/000/091/138/medium800/circuitpython_pic_6.png?1589403394)

It looks like the Black formatting check is now passing, but Pylint isn't. You can find out how to use Pylint in the [Pylint page in this guide](https://learn.adafruit.com/creating-and-sharing-a-circuitpython-library/run-pylint).

![](https://cdn-learn.adafruit.com/assets/assets/000/091/169/medium800/circuitpython_pic_7.png?1589486382)

Now, run Pylint in the your repository's base directory. Make the changes it says to make and re-run Pylint until it says that your code is rated a 10/10. Then, commit and push those changes.

![](https://cdn-learn.adafruit.com/assets/assets/000/091/147/medium800/circuitpython_pic_10.png?1589465521)

<font><font>The PR is still failing, so we should check the Actions workflow output again.</font></font>

![](https://cdn-learn.adafruit.com/assets/assets/000/091/142/medium800/circuitpython_pic_11.png?1589403461)

The good news is that the Pylint check is no longer failing. However, it appears that sphinx is failing. Click on the 'Build Docs' dropdown to see that test's output. It looks like I _inadvertently_ removed some necessary colons.&nbsp;

![](https://cdn-learn.adafruit.com/assets/assets/000/091/143/medium800/circuitpython_pic_12.png?1589403472)

By going into the docs directory, and building sphinx documentation like in the [Sharing Docs on ReadTheDocs page in this guide](https://learn.adafruit.com/creating-and-sharing-a-circuitpython-library/sharing-our-docs-on-readthedocs#sphinx-5-1),&nbsp;we can mirror the output of the 'Build Docs' test. In this case, the issue is in **index.rst**. After fixing those issues, run the command again to verify that the docs are building correctly. In this case, they are, so we can commit and push those changes.

![](https://cdn-learn.adafruit.com/assets/assets/000/091/149/medium800/circuitpython_sphinx.png?1589466362)

<font><font><font><font><font><font><font><font><font><font><font><font><font><font><font><font><font><font><font><font><font><font><font><font><font><font><font><font><font><font><font><font><font><font><font><font><font><font><font><font><font><font>Now that everything is passing and the box has a green border, the PR is ready for review.</font></font></font></font></font></font></font></font></font></font></font></font></font></font></font></font></font></font></font></font></font></font></font></font></font></font></font></font></font></font></font></font></font></font></font></font></font></font></font></font></font></font>

![](https://cdn-learn.adafruit.com/assets/assets/000/091/144/medium800/circuitpython_pic_13.png?1589403489)

# Creating and sharing a CircuitPython library

## Releasing on GitHub

Phew, what a whirlwind. Now that we've got our code on GitHub, our docs on ReadTheDocs and GitHub Actions going we're ready to release!

A release is simply a point in the lifecycle of a software project where the maintainers believe the software is worth trying and using. Pre-release releases are used for cases where the code is in early preview and may still contain bugs. Stable releases typically aren't released with known bugs and are geared towards wide adoption.

Creating or "cutting" a new release is also the point where the maintainer typically provides pre-built or binary releases. In our case, we've setup Actions to automatically provide `mpy` binary files for every release.

To get started, pull up your repo and click the releases button.

![](https://cdn-learn.adafruit.com/assets/assets/000/044/265/medium800/circuitpython_github_releases.png?1501031143)

Once there, click `Create a new release`.

![](https://cdn-learn.adafruit.com/assets/assets/000/044/266/medium800/circuitpython_github_no_releases.png?1501031181)

Now, decide on a version number for this release. CircuitPython's tools require [Semantic Versioning](https://semver.org/) (shortened to SemVer) to work. SemVer is three numbers separated by periods. This is not the same as a decimal number since there are two periods. Anything less than `1.0.0` is typically pre-release and anything after marked `alpha`, `beta` or `rc` for release candidate is also. For example, `1.0.0-rc.1` would be the first release candidate for `1.0.0` and be pre-release. Normal releases would be something like `0.10.0`. Do not start the version with a `v`.

The first number should be incremented and others reset to 0 when the library was changed in a way that may require old code to be rewritten. Changing public function names for example would cause `1.3.0` to become `2.0.0`.

The second number should be incremented, leaving the first and setting the third to zero if new functionality was added but the existing code is compatible. For example add a new function would lead from `1.3.3` to `1.4.0`.

Lastly, the third number should be incremented if existing functionality was fixed. For example a function released in `1.4.0` didn't always work as expected and it was fixe in `1.4.1`. 

Once you decide on a version number enter it into the top box. This will create a git tag that will mark when in the git history the release occurred. You can also do this from git and then refer to an existing tag here.

Next, fill in the title and give the release a description including pointers to other related resources.

![](https://cdn-learn.adafruit.com/assets/assets/000/044/269/medium800/circuitpython_github_new_release.png?1501031895)

The release description uses Markdown for formatting and you can preview what it would look like after the save using the Preview tab. Here is a copy of the release description so you can start with it!

```
Adds basic `hello` functionality.

To use in CircuitPython, download the .mpy file and copy it to the `lib` folder on the `CIRCUITPY` drive. Or, simply install the [Community bundle](https://github.com/adafruit/CircuitPython_Community_Bundle).

Read the [docs](https://circuitpython-example.readthedocs.io/en/latest/) for info on how to use it.
```

![](https://cdn-learn.adafruit.com/assets/assets/000/044/270/medium800/circuitpython_github_new_release_preview.png?1501031952)

Info: 

Now that everything looks good, lets publish the release. Don't forget to tick the pre-release box (under the arrow) if you want this to be marked as such.

![](https://cdn-learn.adafruit.com/assets/assets/000/044/271/medium800/circuitpython_github_publish_release.png?1501032045)

After you hit publish you'll be taken to the release page on GitHub. This is a great place to link folks to so they can get all of the latest information on the release. GitHub includes zip files of the full source code by default.

![](https://cdn-learn.adafruit.com/assets/assets/000/044/272/medium800/circuitpython_github_release_published.png?1501032386)

The release also triggers a few release workflows from Actions. Navigate to the Actions page to check on them. Everything should be green.

Info: 

![](https://cdn-learn.adafruit.com/assets/assets/000/091/159/medium800/circuitpython_Screenshot_from_2020-05-14_11-57-39.png?1589471871)

![](https://cdn-learn.adafruit.com/assets/assets/000/044/274/medium800/circuitpython_github_release_mpy.png?1501032425)

Once Actions is green, the release should be updated with the **.mpy** file for the release. This file can then be copied to the **CIRCUITPY** drive to be used.

The other way folks can use the library is by downloading it as a part of a larger bundle. We have the [Adafruit bundle](https://github.com/adafruit/Adafruit_CircuitPython_Bundle) for officially supported libraries and the [Community bundle](https://github.com/adafruit/CircuitPython_Community_Bundle) for those created and maintained by the larger community.

## Common Release Failures

Almost all release failures happen in the 'Build and Publish' section of the 'upload-pypi' workflow. Here are the two most common ones.

`HTTPError: 400 Client Error: File already exists.`

This happens when there is no change in the driver since the last release. This usually happens when something needs to be re-released with no changes for some reason, often the incorrect semver version being used. The easiest way to fix this is to make a tiny change in a docstring or comment in the driver file and re-release.

`HTTPError: 403 Client Error: Invalid or non-existent authentication information.`

This happens when PyPi credentials haven't been added. It's possible that library shouldn't be on PyPi, so be sure of that before adding credentials. To get that fixed, create an issue on GitHub and assign it to _CircuitPythonLibrarians_, and briefly explain the issue, linking to the Release Action.

`Error: unexpected status code: 403 {"message":"Resource not accessible by integration"` while uploading a release

This happens when the workflow on github is not allowed write access to the repository. To fix this, go to "repository settings". On the left, open the "actions" section and click "general". Finally, under "workflow permissions" allow "read and write permissions". This is necessary in order to allow uploading files to a GitHub release from the release action.

# Creating and sharing a CircuitPython library

## Sharing in a Bundle

We've created a couple library bundles for CircuitPython. The idea is that one can download all available libraries once and then copy them onto the CIRCUITPY drive as needed.

Another advantage of the bundle is that it makes it easy to find a list of all libraries. Even if you can't store all of the libraries on the drive, for example the Gemma M0 is only 64kb, the bundle makes it easy to copy over what you need when you need it.

It also helps CircuitPython devs keep track of libraries and update them as things change.

So, lets get our example library into the community bundle. To do so, we'll make a pull request on GitHub. First, lets fork the community bundle.

![](https://cdn-learn.adafruit.com/assets/assets/000/044/335/medium800/circuitpython_github_overview_fork.png?1501093592)

After the fork is done, the repo will look the same except the repo name will be different at the top left.

![](https://cdn-learn.adafruit.com/assets/assets/000/044/336/medium800/circuitpython_github_overview_forked.png?1501093641)

Next we'll clone the repo to our computer. See [this guide](https://learn.adafruit.com/an-introduction-to-collaborating-with-version-control/os-x?view=all#submitting-a-pull-request-on-github) for detailed instructions on cloning and submitting a pull request. Consider this a quickstart.

When cloning, make sure that you add `--recursive` to get all of the submodules too.

![](https://cdn-learn.adafruit.com/assets/assets/000/044/337/medium800/circuitpython_git_clone_recursive.png?1501094253)

## Adding the submodule to the Library Bundle

Now to add our library to the bundle we'll do:

`git submodule add URL_TO_LIBRARY_INCLUDING.git libraries/SUBFOLDER/lowercase_library_name`

You'll change the following:

- `URL_TO_LIBRARY_INCLUDING.git`&nbsp; - This will be the full URL to the library including the `.git` on the end. To get this URL, click on "Clone" in the GitHub repo, choose HTTPS, and copy that URL. Or you can copy the URL from the repo and add the `.git`.  

  - Example: `https://github.com/adafruit/Adafruit_CircuitPython_PyPortal.git`

- `SUBFOLDER` - The subfolder will be `helpers` or `drivers`. Choose which based on your library. Is it a driver? Choose `drivers`. Is it a helper? Choose `helpers`.
  - Example:&nbsp; `libraries/helpers/` or `libraries/drivers/`

- `lowercase_library_name` - This is the name of your library, minus the `Adafruit_CircuitPython_`, in lowercase letters.
  - Example: `pyportal`

FULL EXAMPLE:

- `git submodule add https://github.com/adafruit/Adafruit_CircuitPython_PyPortal.git libraries/drivers/pyportal`

And then we'll verify it worked by looking at the build log and versions file.

![](https://cdn-learn.adafruit.com/assets/assets/000/044/338/medium800/circuitpython_bundle_add_submodule.png?1501095378)

## Updating the Library List

Before we submit a pull request with our new library, lets update the bundle documentation to include a link to the documentation for our library. Use whichever method you wish to open `/docs/drivers.rst` for the Adafruit bundle or `circuitpython_community_library_list.md` for the community bundle.

![](https://cdn-learn.adafruit.com/assets/assets/000/071/329/medium800/circuitpython_drivers_rst_landscape.png?1550285081)

In library listing, choose one of the categories to place your documentation link. The _current_ categories are: Board-specific Helpers, Helper, Blinky, Displays, Real-time Clocks, Motion Sensors, Environmental Sensors, Light Sensors, Distance Sensors, Radio, IO Expansion, and Miscellaneous. Or, you can create a new category if that best describes your library.

With your category chosen, simply add a new line to that section using the file's hyperlink format. The easiest way, is to copy an existing line and edit it to change both the link text and the URL.

Info: 

Next, lets do our normal commit and push process for a new branch.

![](https://cdn-learn.adafruit.com/assets/assets/000/044/339/medium800/circuitpython_bundle_push.png?1501095654)

Going to GitHub will show an easy prompt to create the pull request.

![](https://cdn-learn.adafruit.com/assets/assets/000/044/340/medium800/circuitpython_github_pull_prompt.png?1501096110)

Follow that to see a differences. Make sure that at the top you are comparing against `adafruit/CircuitPython_Community_Bundle` as the base fork and your branch as the head fork. Make sure at the bottom left that maintainers can edit it (for simple fixes before they submit). Then click `Create pull request` at the bottom right.

![](https://cdn-learn.adafruit.com/assets/assets/000/044/341/medium800/circuitpython_github_pull_create.png?1501096416)

This is the last step you need to do. One of the maintainers will come and respond on the pull request. Once they are happy with the request, it will be merged into the bundle and go out with the next release. It'll show up as purple once accepted.

![](https://cdn-learn.adafruit.com/assets/assets/000/044/342/medium800/circuitpython_github_pull_accepted.png?1501096658)

After a library has been merged into the bundle, the version included will be automatically updated once a day. You don't need to make any other changes to the bundle repo. Simply release new versions in the library repository, and within 24 hours the bundle will be updated and released with the new version. 

# Creating and sharing a CircuitPython library

## Ladyada's Checklist

_This is my checklist since there's so many things to do - maybe useful for others but its specifically for people deploying 'official' adafruit libraries_

1. Create Adafruit\_CircuitPython\_sensor in [https://github.com/adafruit](https://github.com/adafruit)
2. Go to git repo **settings** , **manage access** , add **CircuitPython Librarians** team, then set to **Write**
3. Fork to @ladyada
4. `git clone` to M4 board and to personal library folder
5. `cd` to the personal library directory in MSYS2, run `cookiecutter gh:adafruit/cookiecutter-adafruit-circuitpython`

![](https://cdn-learn.adafruit.com/assets/assets/000/110/218/medium800/circuitpython_image.png?1648243205)

1. Move everything from the new directory up one to git repo
2. Remove `README.md`

Open up adafruit\_sensor.py and fix capitalization of first naming after license

Add short description for first .. todo::

Add URL or remove second todo

Remove third todo, uncomment deps

![circuitpython_image.png](https://cdn-learn.adafruit.com/assets/assets/000/061/311/medium640/circuitpython_image.png?1536524303)

Like so!

![circuitpython_image.png](https://cdn-learn.adafruit.com/assets/assets/000/061/312/medium640/circuitpython_image.png?1536524339)

- Open up `requirements.txt` - verify `Adafruit-Blinka` is there, possibly `adafruit-circuitpython-busdevice`. For UART devices, add `pyserial`

![](https://cdn-learn.adafruit.com/assets/assets/000/061/313/medium800/circuitpython_image.png?1536524433)

- Open up README.rst and update the description `.. todo::`

![](https://cdn-learn.adafruit.com/assets/assets/000/061/314/medium800/circuitpython_image.png?1536524517)

- Open up docs/index.rst and remove the **todo**'s

![](https://cdn-learn.adafruit.com/assets/assets/000/061/317/medium800/circuitpython_image.png?1536530850)

- Do first commit & PR
- Write code!
- Add example to **examples/simpletest.py** empty file
- Run pylint on **adafruit\_sensor.py** and **examples/simpletest.py**
- Once those pass, do another commit
- Put example in README.rst like so:

![](https://cdn-learn.adafruit.com/assets/assets/000/061/315/medium800/circuitpython_image.png?1536530242)

# Creating and sharing a CircuitPython library

## Typing Information

Python is considered a dynamically-typed language, meaning that the type of a variable can change during runtime if its value is changed to something else. For example:

```python
x = 7
print(type(x)) # prints: &lt;class 'int'&gt;
x = "Hello World"
print(type(x)) # prints: &lt;class 'str'&gt;
```

In the code above, the variable `x` contains an integer type value when it is originally set. Afterward, the value is changed to `"Hello World"`, which is a String type value. Ordinarily when we write Python code, we do not need to declare the specific types of the variables; instead the Python interpreter will keep track of the current type internally, based on the values that we assign to the variable.

The opposite of this would be a statically-typed language such as C or Java. In a statically-typed language, the developer must explicitly declare the type when a variable is created, and the type is not able to change simply by changing the value. If the user wants the type to change, they must use a specific conversion or casting function to create a new variable of the desired type.

## What is Typing Information?

Type hints are an optional extra bits of code that declare the intended types for function arguments and returns.&nbsp; Adding type hints to Python code is basically sharing the developers intentions in the code. It is stating something like "the person who wrote this code says that this variable is a String and should always remain a String. If you are writing code that interacts with this it is safe for you to assume this variable will be a String." Since Python is a dynamically-typed language, it is not required for us to declare types like this, but there are some benefits of doing so even though it isn't required.

## Benefits of Typing

The Python and CircuitPython interpreters ignore typing information. So it will make no difference when your code is running whether or not you've included types. The real benefits of typing are felt during the development phase, rather than at runtime. Types allow you, the developer, to be more certain about what kinds of values are expected to be contained by variables. This can make it easier to spot bugs in the code before it's executed. For instance:

```python
x = "Hello World"
print(x.lower()) # prints: hello world
x = 7
print(x.lower()) # raises AttributeError
```

In the code above, the `x` variable is initially assigned the value `"Hello World"`, which is a String type. In Python, Strings have a function called `lower()` that returns the lower-cased version of the String. The first `print()` statement executes successfully. Afterward the value is changed to `7`, an integer type which does not contain the `lower()` function. So when the second `print()` statement tries to execute, it causes an `AttributeError` to be raised, which will crash the program if it's not caught. Type hints try to help us spot this type of error earlier, ideally before we've even attempted to run the program at all. IDEs and other programs can parse the code with type hints and attempt to warn us ahead of time when situations like this arise.

![](https://cdn-learn.adafruit.com/assets/assets/000/110/522/medium800/circuitpython_type_warning.png?1649170823)

In the image above, PyCharm IDE has highlighted part of the code and shows a warning&nbsp; to the programmer that they are attempting to access the `lower()` function, which does not exist for the integer type; it's likely to cause an error if it's executed before resolving this problem. PyCharm and other IDEs use the typing information provided to double check our work and warn us when we accidentally try to use a variable in an invalid way.

## The Syntax of Type Hints
So now you know what type hints are and why they are beneficial, but how do you actually add type hints to your code? For CircuitPython libraries, we are primarily concerned with types for function arguments and returns. CPython (desktop computer Python) supports other types beyond them. But these are the ones we are focused on right now.

### Function Argument Types

To add types to function arguments, we code a colon `:` after the argument name and then put the type after that. For example:

```python
def unhexlify(hexstr: str):
    # code that would be here isn't relevant to the example
    pass
```

In the example above, the argument `hexstr` has its type declared as `str`, which is short for String. So the type hint is indicating that when you call `unhexlify()`, you should always pass it a String type variable as the `hexstr` argument. If there is more than one argument, then each argument should get its own colon and type declaration; if you are providing default values for the arguments, they are coded with a equals sign after the type. Here is another example showing a more complex function with its arguments typed:

```python
def __init__(
        self,
        i2c_bus: busio.I2C,
        address: int = 0x0C,
        gain: int = GAIN_1X,
        resolution: int = RESOLUTION_16,
        filt: int = FILTER_7,
        oversampling: int = OSR_3,
        debug: bool = False,
    ):
```

In this example, there are 5 integer type arguments, 1 boolean type argument, and 1 busio.I2C object argument. Many of the arguments are given default values of constant variables declared in the class that this comes from. Note that the first argument `self` does not receive a type.

### Function Return Types

To code function return types, we use a hyphen and an angle bracket to make an arrow that points to the type that will be returned by the function. This is coded after the parentheses containing the arguments, and before the colon that indicates the beginning of the functions code definition.

```python
def last_status(self) -&gt; int:
	# code that would be here isn't relevant to the example
    pass
```

The above function has been declared to return an integer type value. So any code using this function should be expecting to receive an integer value and should not try to treat it as a String or anything else.

One thing to keep in mind while coding Python classes is that the ` __init__ ()` function always returns the `None` type. Here is an example of an ` __init__ ()` function with its return type coded:

```auto
def __init__(self, spi: busio.SPI, cs: digitalio.DigitalInOut) -&gt; None:
	# code that would be here isn't relevant to the example
    pass
```

## CircuitPython-Specific Considerations

In CPython ("normal" desktop Python), there is a built-in module called `typing` which contains some helper classes sometimes used when declaring types. `typing` was introduced with Python version 3.5. CircuitPython does not currently have this module, so any code that attempts to import it will raise an `ImportError` if it runs on a CircuitPython microcontroller. This may sound problematic, but it actually ends up working out in our favor. We can catch this exception and ignore it, and since types make no difference at runtime, our code will still work properly. The benefit is that we can use this exception as an indicator that the code is running on a microcontroller and choose to ignore the error without importing any of the other classes used only for typing, which will save some precious RAM on the microcontroller.

```python
try:
	# imports used only for typing
    from typing import Tuple # &lt;- this line causes the error
    
    # any imports below this won't happen if the error gets raised
    from circuitpython_typing import ReadableBuffer
    from busio import I2C
except ImportError:
    pass # ignore the error
```

In the CircuitPython libraries, we code the imports as shown. The first import should always be from the `typing` module. The remaining imports after the first one are other classes that may actually exist in CircuitPython or in CircuitPython-compatible libraries such as `circuitpython_typing`.&nbsp;

By putting the `typing` import first, it will cause the `ImportError` to be raised before any of the classes that might actually exist get imported, preventing them from being imported and consuming RAM that won't be used when the program executes.

## Finding the Correct Types

Now you know why type hints are helpful, and you know the syntax used to code the type hints. But you may be wondering how do you figure out which type an argument or return is supposed to be so that you can code the hint? If you are authoring a brand new library from scratch, you probably will know what types are expected since you are the one writing the code and choosing which things get passed in as arguments and which things get returned from functions.

If you're adding type hints to an existing library, you'll have to put on your detective's hat and look for context clues and other evidence that suggests which type any given argument or return type is supposed to be.&nbsp;

![circuitpython_blinka_magnify_resized.png](https://cdn-learn.adafruit.com/assets/assets/000/110/535/medium640/circuitpython_blinka_magnify_resized.png?1649177345)

## Common Places to Look

No two libraries or functions are the exact same, so there is no "one size fits all" approach to determining types. However there are a few common places to look around in the code that are likely to provide good clues about the types.

### Docstrings

If the code contains docstring comments, this might already have the type listed in them and you can use the type specified in the docstring for the type hint.

```python
def _get_pixel(self, xpos, ypos):
        """
        Get value of a matrix pixel
        :param int xpos: x position
        :param int ypos: y position
        :return: value of pixel in matrix
        :rtype: int
        """
        # code that would be here isn't relevant to the example
        pass
```

In this function, the docstring comment does document all of the arguments and their types as well as the return type. In this case, the detective work is mostly done for us, we just need to put the types specified by the docstring into our type hints using the syntax noted above.

Here's how it would look after adding the type hints:

```python
def _get_pixel(self, xpos: int, ypos: int) -&gt; int:
        """
        Get value of a matrix pixel
        :param int xpos: x position
        :param int ypos: y position
        :return: value of pixel in matrix
        :rtype: int
        """
        # code that would be here isn't relevant to the example
        pass
```

### Function Definition

If there is no docstring comment, or if it does not contain the argument and return types, then you'll have to dive a bit deeper. The next place to look is inside of the function definition. You're looking for the lines of code that use the argument variables, and then from those lines of code you may be able to determine what the type is meant to be.

```python
def scroll(self, delta_x, delta_y):
        if delta_x &lt; 0:
            shift_x = 0
            xend = self.width + delta_x
            dt_x = 1
        else:
            shift_x = self.width - 1
            xend = delta_x - 1
            dt_x = -1
        if delta_y &lt; 0:
            y = 0
            yend = self.height + delta_y
            dt_y = 1
        else:
            y = self.height - 1
            yend = delta_y - 1
            dt_y = -1
```

In this function, we see that there are two arguments that we want to find the type for, `delta_x` and `delta_y`. When we look in to the definition code for the function, we see a couple of `if` statements that are comparing `delta_x` and `delta_y` to `0` using the less than operator `<`. We also find some lines of code performing mathematical operations with `delta_x` and `delta_y` such as:

`xend = delta_x - 1`

In this code, it appears that `delta_x` and `delta_y` are being treated as numbers. Numbers can be compared to other numbers with greater than or less than, and they can also be added and subtracted to other numbers. None of the numbers shown contain decimals, and there is no division operator being used, so it's a good guess that these numbers are specifically integers which use the abbreviation `int` for the type. If we found decimals, or if the variable we are trying to type came from the division operator such as:

`some_var = 100/3`

then it may be more likely that our numbers could be of the `float` type rather than `int`. You'll have to look for context clues in the code like this to try to make the most educated guess possible based on the information you find.

### Function Usage

If you don't find concrete evidence in the function definition, another place to look is in the code that is calling the function. This might be inside of an example file, or perhaps in another source code file within the library if it is a multi-file package; sometimes it could even be a usage in the same file where it's defined, but just in a different area of code within that file. Try using the find utility with the function in your code editor to search for usages. Some of the more advanced IDEs even provide a specific "Function Usages" menu item when you right click a function name that will parse all of the project files and show you a list of every found usage. Here is an excerpt from an example script:

```auto
text_to_show = "Hello world"
text_area = bitmap_label.Label(terminalio.FONT, text=text_to_show)
```

In this piece of code, we see a variable named `text_to_show` which has been given the value of `"Hello World"`. We know that `"Hello World"` is a String because it has quotes around it which is exactly how strings are coded.

Next we see that `text_to_show` has been passed to the `Label()` initializer function as an argument named `text`. So from this, we can conclude that the argument named `text` has the type of `string`.

In this case, the variable names also contain a clue: since they both mention the word "text", we know that Strings in Python store text values, so this is further evidence that suggests `string` is the correct type.

# Creating and sharing a CircuitPython library

## Archived

These pages discuss processes no longer used. We are maintaining them for posterity.

# Creating and sharing a CircuitPython library

## Testing with Travis CI

Danger: 

One of the biggest challenges for software projects of all kinds is ensuring the software works as expected. This isn't as simple as it seems, particularly when multiple people are working on a project at once. Each person has different expectations about what the code should do and different testing setups.

Testing is used to make sure that each person's expectations are compatible and that the software still meets those expectations. Often testing is overlooked out of an urge for expediency. However, its well understood that, in the long term, good testing can help increase the development pace of a library. This is due to the confidence gained from tests that validate all expected functionality. It allows a maintainer to know immediately whether a requested change breaks any existing expectations or not. If it doesn't break anything, then its much easier to accept into the existing code.

Info: 

Unfortunately, testing can be complicated and is worth covering in another full guide. This is especially true with CircuitPython where code is running on a microcontroller rather than a full operating system. For now, we'll just cover the basics of setting up Travis.

## Travis CI
A cornerstone of good testing practice (and the Not Rocket Science Rule) is continuous integration (CI for short). CI is the process of automatically running tests on every change to a source code repository. In our case, we'll hook up Travis CI to our GitHub repo so that it tests each commit including proposed pull requests. Furthermore, it will automatically create an mpy file for each of our releases.

To get started with Travis, visit [the website](https://travis-ci.org/) and sign up. Once signed in, you'll see a list of currently active repositories.

Once there, click the plus to activate your new repository.

![](https://cdn-learn.adafruit.com/assets/assets/000/044/248/medium800/circuitpython_travis_overview.png?1501024983)

Now scroll down the page until you see the new repo and click the switch on the left to activate it.

![](https://cdn-learn.adafruit.com/assets/assets/000/044/249/medium800/circuitpython_travis_add_top.png?1501025156)

![](https://cdn-learn.adafruit.com/assets/assets/000/044/250/medium800/circuitpython_travis_activate.png?1501025168)

Now that its activated, click the repo name to see its status.

![](https://cdn-learn.adafruit.com/assets/assets/000/044/251/medium800/circuitpython_travis_activated.png?1501025254)

Now from that page click `Build History`. Nothing should appear yet, so do a small commit (Add more to your README for example) to test that its set up correctly. Refresh the build history tab and you should see a yellow pending build. Once it completes, it should be green.

![](https://cdn-learn.adafruit.com/assets/assets/000/044/252/medium800/circuitpython_travis_build_history.png?1501025882)

![](https://cdn-learn.adafruit.com/assets/assets/000/044/253/medium800/circuitpython_travis_build_ok.png?1501025901)

## Releasing mpys
mpy files are convenient, binary Python files that are easier to load when memory is tight. Travis can automatically build them and attach them to a release. The `.travis.yml` file already has everything configured except for permissions to attach files to releases. :-) (Shout out to TonyD for adding this support.)

Warning: 

To give Travis rights to attach files to our releases we'll need to generate a personal access token for GitHub. **Never ever share these tokens with anyone!** They allow anyone to act as you on GitHub in limited scopes.

To generate a new one, head to your GitHub settings and select `Personal Access Tokens`.

![](https://cdn-learn.adafruit.com/assets/assets/000/044/254/medium800/circuitpython_github_settings.png?1501026407)

Danger: 

![](https://cdn-learn.adafruit.com/assets/assets/000/044/255/medium800/circuitpython_github_settings_lower.png?1501026424)

Once there you will see everything that you've already generated an access token for. The tokens themselves aren't ever visible after they are generated. Instead, you can always generate a new token. Delete any tokens you aren't using currently or those that may have accidently been shared.

For now, we'll generate a new token.

![](https://cdn-learn.adafruit.com/assets/assets/000/044/256/medium800/circuitpython_github_token_list.png?1501026790)

Now fill out the description so that you know what the key was used for and can tell in the future if you still need it.

Also tick the box next to `public_repo` this gives anyone with the token access to all of your public repos so be very careful with the token. We trust Travis CI so its ok to give it to them.

Lastly, scroll down and click `Generate Token`.

![](https://cdn-learn.adafruit.com/assets/assets/000/044/257/medium800/circuitpython_github_token_description.png?1501026919)

![](https://cdn-learn.adafruit.com/assets/assets/000/044/258/medium800/circuitpython_github_token_generate.png?1501027034)

This will take you back to the token list with a new entry in green with the token visible. Click the copy button to copy it to keyboard and switch back to the Travis tab. If you lose the token, you can regenerate a new one by clicking edit.

![](https://cdn-learn.adafruit.com/assets/assets/000/044/259/medium800/circuitpython_github_token_generated.png?1501027237)

Danger: 

Now that we have a code, lets set it up in Travis. Navigate to the build's settings by clicking `Settings` under `More Options`. Scroll down to the `Environment Variables` section and enter it with the name `GITHUB_TOKEN` and the copied token as the value. Leave `Display value in build log` `OFF` otherwise you'll share the token with the world. Lastly, click Add.

![](https://cdn-learn.adafruit.com/assets/assets/000/044/260/medium800/circuitpython_travis_repo_settings.png?1501027806)

![](https://cdn-learn.adafruit.com/assets/assets/000/044/261/medium800/circuitpython_travis_settings_env.png?1501027824)

After clicking Add we should see a new entry for GITHUB_TOKEN with our token obscured. Don't worry, it saved ok. They are just doing their best to keep it secret too. Time to release!

![](https://cdn-learn.adafruit.com/assets/assets/000/044/262/medium800/circuitpython_travis_env_set.png?1501028017)

Also, don't forget to close or refresh the GitHub tab with your token. You don't want to accidently share it in later screenshots. Refreshing will show our description rather than the token.

![](https://cdn-learn.adafruit.com/assets/assets/000/044/263/medium800/circuitpython_github_token_refresh.png?1501028108)

Danger: 


## Featured Products

### Circuit Playground Express

[Circuit Playground Express](https://www.adafruit.com/product/3333)
 **Circuit Playground Express** is the next step towards a perfect introduction to electronics and programming. We've taken the original Circuit Playground Classic and made it even better! Not only did we pack even more sensors in, we also made it even easier to...

In Stock
[Buy Now](https://www.adafruit.com/product/3333)
[Related Guides to the Product](https://learn.adafruit.com/products/3333/guides)
### Adafruit Feather M0 Express

[Adafruit Feather M0 Express](https://www.adafruit.com/product/3403)
At the Feather M0's heart is an ATSAMD21G18 ARM Cortex M0+ processor, clocked at 48 MHz and at 3.3V logic, the same one used in the new&nbsp;[Arduino Zero](https://www.adafruit.com/products/2843). This chip has a whopping 256K of FLASH (8x more than the Atmega328 or 32u4) and...

Out of Stock
[Buy Now](https://www.adafruit.com/product/3403)
[Related Guides to the Product](https://learn.adafruit.com/products/3403/guides)
### Adafruit METRO M0 Express - designed for CircuitPython

[Adafruit METRO M0 Express - designed for CircuitPython](https://www.adafruit.com/product/3505)
Metro is our series of microcontroller boards for use with the Arduino IDE. This new **Metro M0 Express** board looks a whole lot like our&nbsp;[original Metro 328](https://www.adafruit.com/product/2488), but with a huge upgrade. Instead of the ATmega328, this Metro...

In Stock
[Buy Now](https://www.adafruit.com/product/3505)
[Related Guides to the Product](https://learn.adafruit.com/products/3505/guides)
### Adafruit GEMMA M0 - Miniature wearable electronic platform

[Adafruit GEMMA M0 - Miniature wearable electronic platform](https://www.adafruit.com/product/3501)
The **Adafruit Gemma M0** is a super small microcontroller board, with just enough built-in to create many simple projects. It may look small and cute: round, about the size of a quarter, with friendly alligator-clip sew pads. But do not be fooled! The Gemma M0 is incredibly...

In Stock
[Buy Now](https://www.adafruit.com/product/3501)
[Related Guides to the Product](https://learn.adafruit.com/products/3501/guides)

## Related Guides

- [Adafruit Feather M0 Express](https://learn.adafruit.com/adafruit-feather-m0-express-designed-for-circuit-python-circuitpython.md)
- [Adafruit Metro M0 Express](https://learn.adafruit.com/adafruit-metro-m0-express.md)
- [Adafruit Circuit Playground Express](https://learn.adafruit.com/adafruit-circuit-playground-express.md)
- [Crickit Powered Mini Chair Swing Ride!](https://learn.adafruit.com/mini-chair-swing-ride.md)
- [Extending CircuitPython: An Introduction](https://learn.adafruit.com/extending-circuitpython.md)
- [CircuitPython with Jupyter Notebooks](https://learn.adafruit.com/circuitpython-with-jupyter-notebooks.md)
- [MicroBlocks Circuit Playground Express Ornament](https://learn.adafruit.com/microblocks-circuitplayground-express-ornament.md)
- [Cartoon Network MakeCode: Garnet's Palm Gems from Steven Universe](https://learn.adafruit.com/cartoon-network-makecode-garnet-s-palm-gems-from-steven-universe.md)
- [Sipping Power With NeoPixels](https://learn.adafruit.com/sipping-power-with-neopixels.md)
- [Cardboard Circuit Playground Express Inchworm Robot](https://learn.adafruit.com/cardboard-robot-inchworm.md)
- [Stand-alone programming AVRs using CircuitPython](https://learn.adafruit.com/stand-alone-programming-avrs-using-circuitpython.md)
- [Starduino: 8-Bit Super Mario Tree Topper](https://learn.adafruit.com/starduino-neopixel-8-bit-mario-star-tree-topper.md)
- [CircuitPython Basics: Analog Inputs & Outputs](https://learn.adafruit.com/circuitpython-basics-analog-inputs-and-outputs.md)
- [Best Beginner Boards for Teachers](https://learn.adafruit.com/best-beginner-boards-for-teachers.md)
- [Circuit Playground or Hallowing Jack-o'-Lantern](https://learn.adafruit.com/circuit-playground-jack-o-lantern.md)
- [¡Bienvenido a CircuitPython!](https://learn.adafruit.com/bienvenido-a-circuitpython-2.md)
- [Crickit Carnival Bumper Bot](https://learn.adafruit.com/crickit-carnival-bumper-car-bot.md)
- [Circuit Playground Express USB MIDI Controller and Synthesizer](https://learn.adafruit.com/cpx-midi-controller.md)
