Library / SDK
niklasf/python-chess avatar
niklasf/python-chess

python-chess: a Python library for board state, PGN, tablebases and engine control

A chess library for Python, with move generation and validation, PGN parsing and writing, Polyglot opening book reading, Gaviota tablebase probing, Syzygy tablebase probing, and UCI/XBoard engine communication

2,882 stars587 forksPythonGPL-3.0

At a glance

What is it?
python-chess handles move generation, validation, PGN, Polyglot, Gaviota and Syzygy probing, and UCI/XBoard engine talks. It is a library for people writing chess tooling in Python, not a GUI and not an engine.
Who is it for?
Adopt python-chess when your chess work already lives in Python and you need correctness on rules, PGN or tablebase probing rather than raw search speed. Do not adopt it as a GUI, as a UCI engine, or as a way to make Python play strong chess on its own; it talks to engines instead of being one.
Can I use it commercially?
Yes, with conditions. GPL-3.0 is a copyleft licence: if you distribute software that includes it, you must release that software's source code under the same licence. Running it internally without distributing it does not trigger that obligation.
Is it still maintained?
Yes. The repository last received commits 41 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 September 24, 2026, and from our analysis. They are not legal advice.

Editorial analysis

What python-chess actually solves for a Python codebase

Writing chess rules by hand is a known trap. Castling rights, en passant, the halfmove clock, threefold and fivefold repetition, the seventy-five move rule, insufficient material: each one is small, and each one is a place where a hand-rolled board drifts from the laws of chess. python-chess exists so you do not write that layer. The README describes it as a chess library for Python with move generation, move validation, and support for common formats, and the feature list backs that up with checkmate, stalemate and insufficient material detection, repetition detection, and a halfmove clock.

The intended user is a programmer, not a player. If you are building a PGN pipeline, an opening book reader, a tablebase prober, a training tool, or a harness that drives a UCI engine from Python, this is the layer under your code. The docs split along exactly those lines: core, PGN, Polyglot, Gaviota, Syzygy, engine, variants. If your goal is a desktop board with drag and drop, this is not that, and the repository does not ship one.

Board, move stack and the data model behind it

The central object is chess.Board. It holds a position, and it exposes legal moves as a generator rather than a list you build yourself:

python
>>> import chess

>>> board = chess.Board()

>>> board.legal_moves  # doctest: +ELLIPSIS
<LegalMoveGenerator at ... (Nh3, Nf3, Nc3, Na3, h3, g3, f3, e3, d3, c3, ...)>
>>> chess.Move.from_uci("a8a1") in board.legal_moves
False

Moves are pushed and popped, so the same Board object walks a game forward and back. The README shows push_san returning the parsed Move and pop undoing the last one. That stack is what makes search and analysis code possible without cloning positions at every node: you mutate one board, recurse, then pop.

Around the board sit the format layers. FEN and Shredder FEN for position exchange, EPD for test suites with operations like bm and id, SAN for human-readable moves, and PGN for whole games with headers, comments, NAGs and a tree of variations. Tablebase probing is split by format, Gaviota and Syzygy, and engine communication is a separate module covering UCI and XBoard. The repository layout matches: chess/ for the package, data/ for bundled data used by the tests and examples, docs/, examples/, fuzz/, and a python-chess-stub/ directory alongside the mypy typings the README advertises.

Installing python-chess and playing through a game

The README states the requirement plainly: Python 3.8 or later, and the distribution is installed from PyPI under the name chess, not python-chess. setup.py enforces the interpreter floor and raises an ImportError that names the last Python 2 compatible branch, 0.23.x, for anyone on an old interpreter.

bash
pip install chess

The import name is chess, so a first script starts there. The Scholar's mate from the README is the shortest real use: create a board, push moves in SAN, then ask for the game outcome.

python
>>> board.push_san("e4")
Move.from_uci('e2e4')
>>> board.push_san("e5")
Move.from_uci('e7e5')
>>> board.push_san("Qh5")
Move.from_uci('d1h5')
>>> board.push_san("Nc6")
Move.from_uci('b8c6')
>>> board.push_san("Bc4")
Move.from_uci('f1c4')
>>> board.push_san("Nf6")
Move.from_uci('g8f6')
>>> board.push_san("Qxf7")
Move.from_uci('h5f7')

After the last move, is_checkmate returns True, and outcome returns an Outcome whose termination is Termination.CHECKMATE with winner True. Printing the board gives the ASCII diagram from the README, and board.fen() returns the same position as a FEN string. In IPython or Jupyter the board renders as an SVG diagram, which the README documents as notebook integration.

Reading a Polyglot book and where the format modules stop

The format modules are narrow on purpose. Polyglot books are read, not written: the README says reads Polyglot opening books, and the example opens a .bin file, calls find on a board, and reads move and weight from the entry.

python
>>> import chess.polyglot

>>> book = chess.polyglot.open_reader("data/polyglot/performance.bin")

>>> board = chess.Board()
>>> main_entry = book.find(board)
>>> main_entry.move
Move.from_uci('e2e4')
>>> main_entry.weight
1

>>> book.close()

The result is a Move and an integer weight, and the reader is closed explicitly. The same restraint applies elsewhere. Engine communication means speaking UCI or XBoard to a process you supply; python-chess does not include an engine. Tablebase probing means querying Gaviota or Syzygy files you already have on disk. Nothing in the README suggests the library downloads books or tablebases for you, so the data path is yours to manage.

Where python-chess is the wrong tool

The clearest boundary is performance. python-chess is pure Python, and it is a rules and formats library. If you need to search millions of nodes per second, you run Stockfish and talk to it over UCI; the library is the client side of that conversation, not the search. Choosing python-chess as the engine is a category error.

The second boundary is scope. There is no GUI here. The README lists variants, typings, notebook rendering, ASCII boards and the format modules; it does not list a window, a board editor or a clock. If you want a screen, you build it or use something else.

A third constraint is the API surface itself. Board is mutable and the move stack is stateful, which is efficient but means a Board passed into a helper can come back changed. The README shows push and pop as the intended pattern; code that shares one Board across threads or across callbacks without copying needs to think about that. The README does not document thread safety, and I would not assume it.

python-chess compared with talking to Stockfish directly

The most common alternative is not another Python library; it is driving Stockfish over the UCI protocol yourself. The difference is where the chess knowledge lives. If you write the protocol by hand, you own parsing info lines, managing the engine process, reading depth and score, and converting positions to FEN strings. python-chess takes the other approach: it keeps the board model in Python and provides the engine module so the UCI or XBoard conversation is expressed in terms of Board and Move objects rather than raw text.

The trade-off is real. Hand-rolled UCI gives you full control over the process and no dependency at all, which matters if you only ever need to send a FEN and read a bestmove. python-chess costs you a GPL-3.0 dependency and a Python rules layer you may not otherwise need, but it removes the FEN plumbing and gives you legality checking on the way in and out. For a one-off script, the direct route is fine. For anything that keeps positions, replays games, or validates moves, the library earns its place.

Licence, releases and what upgrading costs

python-chess is GPL-3.0, and LICENSE.txt sits at the top of the repository. That is a copyleft licence, which is a materially different proposition from a permissive one if you distribute software that links against it. I am not giving legal advice; the point is that the licence choice is part of the adoption decision, not an afterthought, and it should be read before the dependency goes into a product.

On maintenance, the last push to master was on 2026-08-22, which is recent. The latest release listed is v1.11.2 from 2025-02-25, after v1.11.1 in October 2024 and v1.11.0 earlier that month. The gap between the last release and the last commit is normal for a library that ships when there is something to ship, but it does mean that fixes landing on master may not be in the version you install from PyPI. Pin a version and read CHANGELOG.rst, which sits next to CHANGELOG-OLD.rst in the repository root, before moving between releases. Note that v1.11.0 is where the Python 3.8 floor arrived, so the changelog entry matters if you support older interpreters.

Editorial conclusion

Adopt python-chess when your chess work already lives in Python and you need correctness on rules, PGN or tablebase probing rather than raw search speed. Do not adopt it as a GUI, as a UCI engine, or as a way to make Python play strong chess on its own; it talks to engines instead of being one. Before committing, verify that your Python is 3.8 or later, read the GPL-3.0 terms in LICENSE.txt against how you ship, and check the changelog for the release you pin.

Frequently asked questions

How do I install python-chess?

Install it from PyPI with pip install chess; the distribution name is chess, not python-chess. The README states it requires Python 3.8 or later.

How do I use the python-chess library to play a game?

Create a chess.Board, push moves onto it, and query the result. The README's Scholar's mate example pushes e4, e5, Qh5, Nc6, Bc4, Nf6 and Qxf7 with push_san, then calls is_checkmate, which returns True.

What is the python-chess library?

It is a chess library for Python with move generation, move validation, and support for common formats. It also covers PGN, Polyglot opening books, Gaviota and Syzygy tablebase probing, and UCI/XBoard engine communication.

Official sources

  1. License: GPL-3.0
  2. niklasf/python-chess on GitHub
  3. Project website
  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/niklasf-python-chess.svg)](https://hysenlabs.com/projects/niklasf-python-chess)