Open-source project
sympy/sympy avatar
sympy/sympy

sympy has pushed code for eighteen months without cutting a release

A computer algebra system written in pure Python

15,087 stars5,694 forksPythonNOASSERTION

At a glance

What is it?
SymPy is a computer algebra system written in pure Python, so symbolic expressions are first-class objects rather than strings parsed by a wrapper. It is a NumFOCUS project, released under a BSD licence, and its default branch was pushed to on 2026-11-01. The gap in its release history is the detail to plan around. The newest published release is 1.14.0 from April 2025, after two release candidates eleven days apart, so anyone installing from a package index is getting code that predates every commit since.
Who is it for?
This suits anyone who needs symbolic manipulation rather than numeric computation, because expressions stay symbolic all the way through and can be differentiated, expanded, solved and then turned into code. Being pure Python is the reason it installs anywhere without a compiler and the reason it is slower on heavy numerical work than a compiled library.
Can I use it commercially?
Check first. The repository uses a licence we do not classify automatically, so read its LICENSE file before any commercial use.
Is it still maintained?
Yes. The repository received new commits within the last day.
What is it written in?
Mainly Python, according to GitHub's language statistics.

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

From daily snapshots

Star history

  • sympy/sympy15,087 ★ · +126
Star history
Repository2026-09-222026-11-14
sympy/sympy15k15.1k

126 Stars in the last 53 days, measured from the daily GitHub snapshot. Compare with other projects

Editorial analysis

The newest release is from April 2025 and the repository has moved on for seventeen months

Read the release list next to the push history and the gap is the whole story.

The three most recent releases are 1.14.0, published 2025-04-27, then a second release candidate on 2025-04-25 and a first on 2025-04-14. So the final release came two days after the second candidate, and the candidate phase itself ran about two weeks.

That was the last release. The default branch has taken pushes since, most recently on 2026-11-01.

So roughly eighteen months of development sit between the newest published version and the current code. Nothing is wrong with that on its own, and the project is plainly alive rather than abandoned. But it means there are two quite different things you can install.

A package index gives you 1.14.0 from April 2025. A repository install gives you eighteen months of unversioned changes.

That has a practical consequence for reproducible builds. If you pin the released version you are pinning to something old but stable. If you track the default branch you are tracking code that has never been through a release, including whatever regressions were fixed or introduced along the way.

So decide which of those you want, and if it is the second one, pin a specific commit rather than the branch.

The default test configuration deliberately skips its own slow tests

The test configuration contains a decision that will surprise anyone who assumes the suite you run is the suite that exists.

The default options for the test runner deselect tests marked as slow and tests marked as very slow. They are not skipped on failure, they are not selected in the first place.

So a run of the test suite on a fresh checkout is a run of the fast subset. The heavy cases exist, they are labelled, and they are excluded by default.

The configuration's markers list declares tooslow alongside nocache_fail and thread_unsafe.

The reasoning is sound.

But it means coverage is opt-in and the opt-in is not obvious. If you are relying on the suite to catch a regression in something expensive to compute, you have to turn it on deliberately, and that applies to your own continuous integration as much as to the project's.

The other configuration detail worth knowing: the test paths are restricted to the library directory and the documentation source, with a comment explaining that otherwise the runner would try to collect tests from the scripts directory as well.

Doctests compare floating point results with a custom tolerance flag

A symbolic library documents itself with examples that print numerical output, and the configuration for those examples contains a flag worth understanding.

Four option flags are set for the documentation examples. Three are standard. The fourth is not: a flag for comparing floats.

With it, the examples compare floating point output within a tolerance rather than exactly.

This is the right choice, and it also means the documentation examples are not a strict assertion of exact values. A documented result is correct to within whatever the tolerance allows, not to the digit.

The other three flags are more familiar. Whitespace is normalised, exception detail is ignored so that a message can change wording without failing, and ellipsis allows a placeholder in expected output.

Combined, those four flags describe a documentation suite that is deliberately tolerant. That is what makes it practical to keep hundreds of examples current in a project of this size.

The consequence for a reader is that a documented output is a strong hint rather than a specification. If you need an exact value, compute it rather than trusting the printed form.

The install script keeps a hand-written module list that must match the package by hand

The packaging script contains a list that a machine can generate and a person has to keep in step.

There is a check at the top that refuses to install on an older interpreter, printing a message naming the required version and the one detected. That is the version floor, enforced in the script, with a comment saying to keep it in sync with sympy/__init__.py and python_requires.

Then there is a module list. It is an explicit enumeration of every module in the package, written out one per line, and the comment above it says to check it against the result of running a generation script in the scripts directory.

So the package's module inventory is maintained by hand, with a script available to produce the correct list and a comment telling you to compare them.

Why a library written in pure Python needs this is a fair question, and the answer is usually packaging rather than necessity. Whatever the reason, it is a place where the distribution can disagree with the source tree, and the project has documented the check rather than automating it in.

There is also an entry point declared for the interactive console, and a comment nearby reminding the reader to keep that section in sync with the package's own initialisation file and with the version requirement.

The version floor is checked in the script itself:

python
if sys.version_info < (3, 10):
    print("SymPy requires Python 3.10 or newer. Python %d.%d detected"
          % sys.version_info[:2])
    sys.exit(-1)

So the consequence is that if you are reading a distribution rather than the repository, the set of modules is whatever the last person synced, not necessarily what is on disk.

The documented way to run the tests is a setup script invocation the packaging world has moved past

The front page's test section leads with a command that has not been the recommended way to do this for a long time.

To run everything, it says to invoke the setup script with a test argument in the current directory. The command as written also carries a stray leading character before the script name, which is a typo rather than a shell feature.

For finer-grained running it points at two scripts in the scripts directory, one for tests and one for doctests. The master branch is tested automatically by GitHub Actions.

The problem is the first instruction. Calling a test command through a packaging script is the pattern the packaging ecosystem deprecated, and doing it means your test run depends on a script that also exists to build a distribution.

The alternatives the same page documents are bin/test and bin/doctest.

So if you are setting up continuous integration for something that depends on SymPy, ignore the setup script command and drive the runner yourself. You will get the same tests, minus the ones the configuration excludes anyway, without depending on a deprecated entry point.

A generated parser is committed to the repository and needs one exact tool version

One feature of this library depends on generated files that are checked in, and the instructions for regenerating them are specific to the point of being fragile.

The parser and lexer for the experimental LaTeX parsing feature were generated with a parser generator toolchain, written into a directory inside the parsing package, and committed to the repository.

Most users never need to touch this. The page says so. But if you intend to work on the feature, you need the generator's command-line tool on your path, and both routes the page gives use version 4.11.1.

The instructions then offer two ways to get that exact version. One is a package manager command from a community channel, with the version pinned. The other is to download the complete archive from the tool's website, export a classpath variable as instructed, and then create a file that runs it with a shell wrapper, because the project prefers an executable on the path rather than an alias.

That is a long set of instructions for regenerating files most people will never regenerate, and it is an accurate description of the maintenance burden on anyone who does.

The practical consequence is that contributing to the LaTeX parsing feature is a setup exercise before it is a coding exercise, and the version pin means the toolchain is not forward compatible without regenerating against whatever the current release is.

The clean instructions delete work, and the front page says so in capital letters

There is a section of the front page that exists entirely to warn you, and it is worth reading as much for the warning as for the commands.

Three commands are given for reverting a tree. One clears everything the ignore file excludes. One clears all untracked files. One reverts the most recent commits.

The warning that follows is in capitals and says these will all clear changes you may have made and that you will lose them forever. It tells you to check with the status, diff and two dry-run variants of the clean command before doing any of them.

That is an unusual amount of care in a readme, and it is there because this is the documentation for a project people clone and work in, where the tree can contain generated files and build output.

The clean variants are a useful distinction to internalise if you are not already familiar with them: one removes what the ignore file matches, which in a generated-code project is a lot, and the other removes untracked files regardless of the ignore rules.

So the consequence is that this repository is written for people who edit it. Treat the clean commands as the last resort the front page says they are, and run the dry-run variants first as instructed.

Editorial conclusion

This suits anyone who needs symbolic manipulation rather than numeric computation, because expressions stay symbolic all the way through and can be differentiated, expanded, solved and then turned into code. Being pure Python is the reason it installs anywhere without a compiler and the reason it is slower on heavy numerical work than a compiled library. Two things to check before you pin it. Which version you are pinning, because the newest release on the index is from April 2025 while the repository has moved on since, so a repository install and a package install are not the same code. And what your test suite does with the slow markers, since the default configuration deliberately skips them. Install from a package index for stability, or pin a specific commit if you need the current tree.

Frequently asked questions

What is SymPy used for?

SymPy is a computer algebra system written in pure Python. It keeps symbolic expressions as objects rather than as text, so you can differentiate, expand, substitute into and solve expressions symbolically, and print results. The front page gives a short example that builds a symbol, takes a reciprocal of a cosine, and prints its series expansion up to order x**10.

Is SymPy free to use?

Yes. The front page states that a New BSD licence covers all files in the repository unless stated otherwise, with the licence file linked. The project's repository metadata reports no detected licence, so machine-readable licence information is absent even though the terms are stated in the readme. SymPy is also a NumFOCUS project.

how to install sympy

Four documented routes. The recommended one is a full Anaconda distribution. There is a package index route with `pip install sympy`, an Anaconda-specific command, and a source route: clone the repository, then run `pip install .` inside it. Tarballs and system packages are covered by a separate installation document. The install script refuses to run on anything older than Python 3.10.

how to use sympy

Import what you need and build expressions from symbols. The front page's example imports a symbol and a cosine, creates a symbol, takes a reciprocal, and prints a series expansion about zero. There is also an interactive console, a thin wrapper around the standard Python console that loads the SymPy namespace and runs some common commands, started with the isympy command once the package is installed.

Official sources

  1. Issues
  2. Project website
  3. README
  4. Releases
  5. sympy/sympy on GitHub
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/sympy-sympy.svg)](https://hysenlabs.com/projects/sympy-sympy)

Star history chart for your README

markdown
[![Star history](https://hysenlabs.com/chart/sympy-sympy.svg)](https://hysenlabs.com/projects/sympy-sympy)

Was this useful?