CLI tool
aisk/pick avatar
aisk/pick

pick: an interactive selection list for the terminal

create curses based interactive selection list in the terminal

794 stars68 forksPythonMIT

At a glance

What is it?
pick is a small Python library for drawing an interactive selection list in the terminal on top of curses, with multiselect, a pluggable rendering backend and a documented way to embed it in an application that already owns the screen. It is MIT licensed, depends on almost nothing, and ships on PyPI.
Who is it for?
pick suits a Python tool that needs one question answered, where a prompt is too plain and a full menu framework is too much: a script installer, a branch picker, a quick chooser inside a longer command. It is the wrong choice if you need a general purpose terminal menu, since the API is one function and a list of options rather than a widget system, and it is the wrong choice for a graphical application that needs real widgets, because a curses list is still a text grid.
Can I use it commercially?
Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
Is it still maintained?
Yes. The repository last received commits 10 days ago.
What is it written in?
Mainly Python, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on October 22, 2026, and from our analysis. They are not legal advice.

Editorial analysis

One function and a list of options

pick is a small Python library to help you create an interactive selection list in the terminal, and the description in the package metadata is blunter still: pick an option in the terminal with a simple GUI.

The API is a single function. You import the module, you pass a list of options and an optional title, and you get back the choice:

python
>>> import pick
>>> title = 'Please choose your favorite programming language: '
>>> options = ['Java', 'JavaScript', 'Python', 'PHP', 'C++', 'Erlang', 'Haskell']
>>> option, index = pick(options, title)
>>> print(option)
>>> print(index)

The outputs in the documentation make the contract clear. For the example above the selected option is C++ and the index is 4, so you get both the value and its position in the list you passed in.

The import style is also documented with a small joke attached. You can also use from pick import pick if you do not want to use cadule's magic, which refers to the cadule package that pick depends on.

The scale of the library is worth noting. It is published on PyPI, supports Python 3.8 through 3.14, and its only hard dependencies are cadule, a small helper library, and windows-curses on Windows. The licence is MIT and the current version is 2.6.0, released on 2026-02-28, with the last push to the repository on 2026-10-13.

For a tool that does one thing, that is a remarkably small surface, and the documentation is correspondingly short enough to read in a minute.

Multiselect returns pairs, and the count is enforceable

The second documented example covers the case where one answer is not enough, and it shows a return shape that differs from the single selection case in a way that is easy to get wrong.

python
>>> from pick import pick

>>> title = 'Please choose your favorite programming language (press SPACE to mark, ENTER to continue): '
>>> options = ['Java', 'JavaScript', 'Python', 'PHP', 'C++', 'Erlang', 'Haskell']
>>> selected = pick(options, title, multiselect=True, min_selection_count=1)
>>> print(selected)

And the output:

python
>>> [('Java', 0), ('C++', 4)]

So a single selection returns a tuple of option and index, while a multiselect returns a list of those tuples. The index is preserved for each item, which is the part that makes the list useful in a script: you get the position of every selection in the original list, not just the values.

The min_selection_count option is the other thing to notice. It dictates a minimum of selected items before continuing, which is how you make the selection mandatory. The title in the example even says so in passing, press SPACE to mark, ENTER to continue, and the default behaviour of a list where SPACE toggles and ENTER confirms is the conventional one.

The default_index option complements it for the other direction. It sets which option is selected if the user presses ENTER without moving, and the documentation is explicit that you use it when the default selected option is not the first one, which is the common case when the most likely answer is not the top of the list.

Options for embedding pick in an existing curses app

The options list is where the library shows its experience with real terminal programs, and four of the entries only make sense if you already have a curses application.

The screen option is the central one. If you are using pick within an existing curses application, you set this to your existing screen object, and it is assumed this has initialised in the standard way, for example via curses.wrapper(), or curses.noecho() followed by curses.cbreak() and screen.kepad(True). In other words, pick does not own the terminal in that mode, it draws on a screen you have already prepared, which is what makes it composable with the rest of your program instead of a modal dialog that takes over the screen.

The position option goes with it. If you are using pick within an existing curses application, you use it to set the first position to write to, with the example given as position=pick.Position(y=1, x=1). Without it, pick starts writing at the top left of the screen and overwrites whatever is there.

The quit_keys option lets the user leave early. You pass key codes, and if the corresponding key is pressed the menu quits.

And the indicator option customises the selection marker, which defaults to an asterisk.

Together these four options are the difference between a prompt that takes over your terminal and a widget you can drop into a program that is already drawing. The examples directory in the repository makes that concrete, with separate scripts for a basic list, a disabled state, multiselect, a custom option, an explicit position and scrolling.

The backend is pluggable, and blessed is the alternative

pick uses the curses backend by default, and the documentation offers an optional alternative.

If you prefer using blessed, install the optional extra:

bash
pip install "pick[blessed]"

and then pass backend="blessed" to pick(...).

The backend option is described as the rendering backend to use, and it accepts three things: the string "curses", which is the default, the string "blessed", which requires the optional install, or a custom Backend instance. That last option is the interesting one for a project that wants its own look, because it means the rendering is an interface rather than a hard coded path.

The two backends are not equivalent in every environment, and the documentation does not spell out the differences beyond the requirement of installing the extra. What it does make clear is the shape of the abstraction, and the fact that blessed is optional rather than required, which keeps the default install of pick to a single small dependency.

The dependency list in the package metadata reinforces that. There is cadule, the helper library that provides the magic behind the module import, and windows-curses, which is pulled in only on Windows so that the curses module is available there. The optional extra adds blessed at 1.17.0 or newer, and the mypy configuration lists both blessed and cadule as modules with missing imports ignored, which is why their imports ignore missing modules.

Packaging with uv and a strict mypy configuration

The development setup of the project is a good indicator of how the library is maintained, and it is recent.

The build backend is uv_build, at a version between 0.11.11 and 0.13, and the repository carries a uv.lock file. The Makefile is small and expresses the whole workflow: publish runs uv build followed by uv publish, test runs uv run mypy . and then uv run pytest, and clean removes the build, dist and egg-info directories.

The type checking configuration is worth a look. check_untyped_defs is on, and so are warn_return_any, warn_unreachable and warn_unused_ignores. That combination is stricter than the defaults, and it is the setting that catches a function that claims to return a specific type while actually returning whatever the underlying call produced. For a library whose whole job is to hand back a tuple of a value and an index, that kind of check is exactly the one you want.

The development dependencies are pytest, mypy and pre-commit, with the pre-commit configuration file at the repository root. The tests live in a tests directory and the example scripts in an example directory, alongside the two animated GIFs the README uses to show the basic and multiselect behaviour.

The release history shows the project moving in fits and starts. The recent tags are v2.6.0 from 2026-02-28, v2.4.0 from 2024-09-15 and v2.4.0b1 from 2024-06-27, so the 2.4 line ran for a while with a beta in front of it, and 2.6 arrived in early 2026.

Editorial conclusion

pick suits a Python tool that needs one question answered, where a prompt is too plain and a full menu framework is too much: a script installer, a branch picker, a quick chooser inside a longer command. It is the wrong choice if you need a general purpose terminal menu, since the API is one function and a list of options rather than a widget system, and it is the wrong choice for a graphical application that needs real widgets, because a curses list is still a text grid. Verify three things before you depend on it: that the return shape matches what you need, because a single selection returns an option and an index while a multiselect returns a list of option and index pairs, that your terminal and your own curses setup, if you have one, initialise the screen the way the library expects, and that the Python version you target is inside the supported range.

Frequently asked questions

What is pick?

It is a small Python library for creating an interactive selection list in the terminal, built on curses by default. It is MIT licensed, published on PyPI, and supports Python 3.8 through 3.14.

How do I install pick, and what is the blessed backend?

Install it with pip install pick. The rendering backend is curses by default; if you prefer blessed, install the optional extra with pip install "pick[blessed]" and then pass backend="blessed" to pick, or supply your own Backend instance.

What does pick return for a single selection and for a multiselect?

A single selection returns the chosen option and its index as a pair, for example C++ and 4. A multiselect with multiselect=True returns a list of such pairs, for example [('Java', 0), ('C++', 4)], and min_selection_count sets how many items must be marked before you can continue.

Can I use pick inside an application that already uses curses?

Yes. Pass your existing screen object as the screen option, assuming it was initialised in the standard way such as via curses.wrapper(), and use position=pick.Position(y=1, x=1) to set where pick first writes so it does not overwrite the rest of your interface. The quit_keys option lets the user exit early with chosen key codes.

Is there a fork or related project for selecting tree data?

The project lists pickpack, a fork of pick to select tree data, among its community projects. The upstream project itself is maintained by wong2 and AN Long, and the current release is 2.6.0.

Official sources

  1. aisk/pick on GitHub
  2. Issues
  3. License: MIT
  4. README
  5. Releases
Add this badge to your README

If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/aisk-pick.svg)](https://hysenlabs.com/projects/aisk-pick)

Was this useful?