Hacking

Johnny Robeson edited this page Sep 5, 2016 · 29 revisions

Getting the Source

The easiest way to get started with the latest beets source is to use pip to install an "editable" package. This can be done with one command:

$ pip install -e git+https://github.com/beetbox/beets.git#egg=beets

Or, equivalently:

$ git clone https://github.com/beetbox/beets.git
$ cd beets
$ pip install -e .

If you already have a released version of beets installed, you may need to remove it first by typing pip uninstall beets. The pip command above will put the beets source in a src/beets directory and install the beet CLI script to a standard location on your system. You may want to use the --src option to specify the parent directory where the source will be checked out and the --user option such that the package will be installed to your home directory (compare with the output of pip install --help).

Alternatively, you can get the source via git at GitHub. The pip method above installs dependencies automatically, but if you do it manually, you can use pip to install the Python modules mutagen, munkres, unidecode, pyYAML, and musicbrainzngs. With that in place, you can just type ./beet in the source directory to run beets from there.

Getting Involved

Consider joining the mailing list for beets. It's fairly low-traffic and is used by both users and developers on the project. Also come hang out in the #beets IRC channel on Freenode.

Contributing as a Non-Programmer

You can help out with the beets project even if you're not a Python hacker! Here are a few ideas:

  • Promote beets! Help get the word out by telling your friends, writing a blog post, or discussing it on a forum you frequent.
  • Improve the documentation. It's incredibly easy to contribute here: just find a page you want to modify and hit the "Edit on GitHub" button in the upper-right. You can automatically send us a pull request for your changes.
  • GUI design. For the time being, beets is a command-line-only affair. But that's mostly because I don't have any great ideas for what a good GUI should look like. If you have those great ideas, please get in touch.
  • Benchmarks. We'd like to have a consistent way of measuring speed improvements in beets' tagger and other functionality as well as a way of comparing beets' performance to other tools. You can help by compiling a library of freely-licensed music files (preferably with incorrect metadata) for testing and measurement.

Contributing Code

If you're interested in helping with the project, the best thing to do is to find something you want beets to do but it doesn't currently do and implement it. Here are some places to get started:

  • We maintain a set of feature requests marked as "bite-sized". These are issues that would serve as a good introduction to the codebase. Claim one and start exploring!
  • Our test coverage is somewhat low. You can help out by finding low-coverage modules on Coveralls and adding new tests.
  • There are several ways to improve the tests in general (see Testing) and some places to think about performance optimization (see Optimization).
  • The API documentation are currently quite spare. You can help by adding to the docstrings in the code and to the documentation pages themselves.

How to Submit Your Work

Do you have a great bug fix, new feature, or documentation expansion you'd like to contribute? Follow these steps to create a GitHub pull request and your code will ship in no time.

  1. Fork the beets repository and clone it (see above) to create a workspace.
  2. Make your changes.
  3. Add tests. If you've fixed a bug, write a test to ensure that you've actually fixed it. If there's a new feature or plugin, please contribute tests that show that your code does what it says.
  4. Add documentation. If you've added a new command flag, for example, find the appropriate page under docs/ where it needs to be listed.
  5. Add a changelog entry to docs/changelog.rst near the top of the document.
  6. Run the tests and style checker. The easiest way to run the full battery of tests is to use tox. This will run the same set of tests that we run on Travis.
  7. Push to your fork and open a pull request! We'll be in touch soon.
  8. If you add commits to a pull request, please add a comment after you push them. (GitHub doesn't notify us when commits are added, so we need your comment so we get an email alert.)

Remember, code contributions have four parts: the code, the tests, the documentation, and the changelog entry. Thank you for contributing!

The Code

The documentation has an API documentation section that serves as an introduction to beets' design. There's also an Architecture page on this wiki.

Coding Conventions

There are a few coding conventions we use in beets:

  • Whenever you access the library database, do so through the provided Library methods or via a Transaction object. Never call lib.conn.* directly. Transaction objects help control concurrent access to the database and assist in debugging conflicting accesses.
  • Always use the future imports print_function, division, and absolute_import, but not unicode_literals. These help keep your code modern and will help in the eventual move to Python 3.
  • str.format() should be used instead of the % operator
  • Never print informational messages; use the logging module instead. In particular, we have our own logging shim, so you'll see from beets import logging in most files.
    • Always log Unicode strings (e.g., log.debug(u"hello world")).
    • The loggers use str.format-style logging instead of %-style, so you can type log.debug(u"{0}", obj) to do your formatting.
      • exceptions must use except A as B: instead of except A, B:

We follow PEP 8 for style. The buildbot and tox will complain if you use tabs for indentation, for example.

Handling Paths

A great deal of convention deals with the handling of paths. Paths are stored internally -- in the database, for instance -- as byte strings (not unicode objects). This is because POSIX operating systems' path names are only reliably usable as byte strings -- even if you request unicode paths, you might still get back bytes. On Windows, the strings are always encoded with UTF-8; on Unix, the encoding is controlled by the filesystem. Here are some guidelines to follow:

  • If you have a Unicode path or you're not sure whether something is Unicode or not, pass it through bytestring_path function in the beets.util module to convert it to bytes.
  • Pass every path name trough the syspath function (also in beets.util) before sending it to any operating system file operation (open, for example). This is necessary to use long filenames (which, maddeningly, must be Unicode) on Windows. This allows us to consistently store bytes in the database but use the native encoding rule on both POSIX and Windows.
  • Similarly, the displayable_path utility function converts bytestring paths to a Unicode string for displaying to the user. Every time you want to print out a string to the terminal or log it with the logging module, feed it through this function.

Editor Settings

Personally, I work on beets with vim. Here are some .vimrc lines that might help with PEP 8-compliant Python coding:

filetype indent on
autocmd FileType python setlocal shiftwidth=4 tabstop=4 softtabstop=4 expandtab shiftround autoindent

Consider installing this alternative Python indentation plugin. I also like Syntastic with its flake8 checker.