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 package is an example of this. Both packages and modules can be called libraries.

By importing 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 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. 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 includes ten 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.
  • .travis.yml - Configures automated testing and release file generation.
  • CODE_OF_CONDUCT.md - Outlines expectations for behavior of contributors to ensure a friendly, welcoming environment. Source from Contributor Convenant.
  • LICENSE - Contains the legal text for the license which allows others to use and modify the code you've written. We default to MIT but others can be used. We recommend 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, Markdown (.md) or reStructuredText (.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, ReadTheDocs and autogenerated documentation.
  • adafruit_{name}.py - Your code! This is the module you can use to organize your code. It automatically includes example license text and comments.
  • api.rst - Triggers the autogeneration of documentation for the above module.
  • conf.py - Configuration settings for Sphinx.
  • readthedocs.yml - Configuration settings for ReadTheDocs
  • 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.
  • setup.py - Required for PyPI deployment. File will be present even if library is not deployed to PyPI.

You may have noticed that the filename of your library is adafruit_{name}.py. The {name} portion will be replaced when the cookiecutter is actually run. We recommend removing the adafruit_ portion and replacing it with a prefix of your own. The adafruit_ prefix means that we fully support the library going forwards.

Installing cookiecutter

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

Download: file
pip install cookiecutter

Running cookiecutter

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

Download: file
cookiecutter gh:adafruit/cookiecutter-adafruit-circuitpython

Prompts

  • 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_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 _.
  • 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.
  • author - Who you are! Sets the copyright to you.
  • github_user - GitHub user or organization which will host this repo. For example, Adafruit funded libraries should say adafruit here.
  • company - Used to give Copyright credit to the company funding the library. For example, Adafruit funded libraries should say "Adafruit Industries" here.
  • depends_on_bus_device - Determines whether to add comments about a dependency on https://github.com/adafruit/Adafruit_CircuitPython_BusDevice. Leave empty if the library won't use BusDevice.
  • depends_on_register - Determines whether to add comments about a dependency on https://github.com/adafruit/Adafruit_CircuitPython_Register. Leave empty if the library won't use Register.
  • other_dependencies - Add any other module dependencies. Enter a comma separated string of modules, using the lowercase full name of the module, using - instead of _  (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.

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 thats easier to load in CircuitPython.

To do so, you'll need to download (or clone) the CircuitPython source, make mpy-cross and run it on your source file.

git clone https://github.com/adafruit/circuitpython.git
cd circuitpython/mpy-cross
make
./mpy-cross example/adafruit_example.py

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

This zip contains mpy-cross binaries for MacOS X, Raspbian, Ubuntu x86 and Windows that will work with CircuitPython 3.x+

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, 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 for details on installing git. Consider this a quick start to initializing a repository (make sure you are in the library's directory).

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.

Temporarily unable to load embedded content:
https://www.youtube.com/watch?v=iNG1a--SIlk
If you've used other source control theres an important distinction between commits in the subversion, cvs and perforce sense and the git sense. In those others, committing is the same as sharing with others. In git, a commit is for you alone until its shared or "pushed". That means you can change commits and revise the history of your code to better reflect your changes and motivations after committing it. This is known as "rewriting history" in git terminology and is super powerful. Its only when a commit is shared with others on a shared branch in a shared repo that it shouldn't be done because other's may have built on top of it already.
This guide was first published on Jul 31, 2017. It was last updated on Jul 31, 2017. This page (Creating a library) was last updated on Nov 17, 2019.