web2py runs from an unzipped tree, and names its successor
Free and open source full-stack enterprise framework for agile development of secure database-driven web-based applications, written and programmable in Python.
At a glance
- What is it?
- web2py is a Python framework you start with one script and never install, with its database layer living in a separate PyDAL submodule. Its own README sends new projects to py4web, and the build files explain why that exit plan is not a small one.
- Who is it for?
- Take web2py only when an application already lives in its tree and the goal is keeping it running, not starting something new, because the project's own text points fresh work at py4web and calls 3.x limited maintenance. Teams whose deliverable is a wheel installed into an existing WSGI stack should skip it, since the framework writes session, cache and upload files next to the code.
- Can I use it commercially?
- Not without permission. GitHub finds no licence file in the repository, and without a licence all rights are reserved by default: you may read the code but not reuse it. Check the README, or ask the authors, before using it.
- Is it still maintained?
- Yes. The repository last received commits 1 day 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 23, 2026, and from our analysis. They are not legal advice.
Editorial analysis
No install step, yet a pip install target in the Makefile
web2py is a full-stack framework for database-driven web applications, written and programmable in Python, and its start procedure is unusually short: unpack the archive and run one file. The installation notes say there is nothing to install beforehand, because the interpreter picks up the framework's own directory as the script starts.
python web2py.pyTwo files sit at the root of that directory. `web2py.py` is the startup script, and `anyserver.py` is the one to use when you would rather run a third-party server. No port, host or login appears in the first-run instructions, so a developer ends up with a default server and nothing to configure.
The root Makefile then complicates that story with targets named `install`, `build` and `deploy`, which publish the project through pip, a build frontend and twine.
install:
python -m pip install .
build:
pip install --upgrade build
pip install --upgrade twine
python -m build
deploy: build
python -m twine upload dist/*None of that is needed to run the framework, and the Makefile makes the point in the target it runs by default, printing that in order to run web2py you do not need to make anything. A root `requirements.txt` names gunicorn and google-cloud-firestore, and the README never says where those two packages are installed on the unzip path, so a deployment that avoids pip entirely has no documented place to get them.
A recursive clone is mandatory because PyDAL is a submodule
The database layer arrives before any application code, and it arrives from a second repository. The Database Abstraction Layer was decoupled into PyDAL in early 2015, and in git terms it sits inside the main repository as a submodule. A clone without the recursive flag therefore skips the step that populates it:
git clone --recursive https://github.com/web2py/web2py.gitFor a checkout that already exists, the one-time repair runs a submodule update:
git submodule update --init --recursivePyDAL then moves on its own schedule. It uses a date-naming scheme similar to Ubuntu, keeps a stable cycle of its own apart from the framework wrapped around it, and takes its bug reports in a separate repository rather than in the framework's issue tracker. Anyone adopting web2py is installing two versioned pieces that tick independently, and the root `.gitmodules` file binding them together is not explained anywhere in the README.
Every application is a directory copied from welcome
Running the server does not hand you a Python package to import. It hands you a directory tree, and an application is a folder the framework owns. The stock layout puts `applications/`, `gluon/`, `handlers/`, `logs/`, `deposit/` and `site-packages/` under one root, with the framework's own `admin/` web based IDE sitting inside `applications/` next to the sample apps.
gluon/ > the core libraries
packages/ > web2py submodules
dal/
contrib/ > third party libraries`welcome/` is the scaffolding app, and the README notes that every app copies it. Inside each application the framework expects the same set of directories: `models/`, `views/`, `controllers/`, `sessions/`, `errors/`, `cache/`, `static/`, `uploads/`, `modules/`, `cron/` and `tests/`. Data definitions live in `models/`, templates live in `views/`, and a request is routed to a controller function that returns what the view renders.
Two consequences follow for anyone planning a deployment. Your code cannot live outside that tree, and the per-app folders are not decoration, since `sessions/`, `cache/` and `uploads/` hold session files, cached fragments and uploaded files as plain files on disk.
Handlers decide which server runs the application
The web layer is a directory of adapters rather than a configuration switch. The `handlers/` directory holds `wsgihandler.py`, which connects web2py to WSGI, plus handlers for Fast-CGI, SCGI, Gevent and others, and that is what `anyserver.py` exists for. Choosing a server means selecting a handler, so one application directory can run under the built-in server in development and under something else afterwards without edits to application code.
The root listing also shows deployment files unrelated to that choice: `docker/`, `binaries/`, `app.yaml`, `gae.nix` and `requirements.gae.txt`, sitting beside a `requirements.txt` naming gunicorn and google-cloud-firestore. Those entries place web2py inside serving stacks the README never walks through, since the installation section covers only the unzip case.
Tests run through the same entry point rather than through a separate runner. The Makefile drives them from `web2py.py`, and its coverage target sets an environment variable before adding a flag:
coverage:
coverage erase --rcfile=gluon/tests/coverage.ini
export COVERAGE_PROCESS_START=gluon/tests/coverage.ini
python web2py.py --verbose --run_system_tests --with_coverage
coverage combine --rcfile=gluon/tests/coverage.ini
coverage html --rcfile=gluon/tests/coverage.iniFive lines from a target that also sleeps a second between the combine and the HTML report. `tox.ini` and `appveyor.yml` sit at the root beside a workflow badge for tests.yml, so the version matrix is declared in the repository instead of in the applications.
make clean wipes session, cache and upload files
The `clean` target in the root Makefile is the sharpest edge in the build file, and it sits one command away from application state:
rm -f routes.py
rm -rf applications/*/sessions/*
rm -rf applications/*/errors/* | echo 'too many files'
rm -rf applications/*/cache/*Read the third line carefully. A recursive delete is piped into `echo`, so the string `too many files` prints on every run whether or not an `errors` directory existed, and nothing ever reaches the pipe because `rm -rf` writes to standard error, not standard output. A few lines earlier the same target ends a `find` with `|| true`, discarding the exit status in the same way.
What the target removes is not only build residue. It deletes `routes.py`, clears `applications/admin/uploads/*`, `applications/welcome/uploads/*` and `applications/examples/uploads/*`, then runs `find` over dotfiles in `admin/`, `examples/` and `welcome/`, where the web based IDE keeps its state. On a working copy, session files, cached fragments, uploads and IDE settings all disappear without a prompt.
The version file is stamped 3.3.3 while the newest tag is v3.1.1
Version numbers here are written into a file by a build target rather than read off a tag. A target named src in the root Makefile rewrites `gluon/version.py` in place, and the string it writes carries a comment about semantic versioning plus a shell timestamp:
src:
echo 'VERSION = "3.3.3-stable+timestamp.'`date +%Y.%m.%d.%H.%M.%S`'"' > gluon/version.pyCompare that with the newest tag and the two do not line up. The highest tag named for the project is v3.1.1, dated 2025-12-22, with v2.27.1 cut the same day as the last version supporting Python 2.7, and 2.19.1 from 2020-03-21 sitting further back. The build file therefore writes 3.3.3 as its starting point while the newest tag starts at 3.1.1, and the README does not say which value a user is meant to quote.
A document that could settle it sits at the root, `versioning-and-support-policy.md`, and the README links to it. Its text is not reproduced there, so anyone who needs a defensible version string for a ticket or a deployment should read that policy before repeating either number.
Why new projects are pointed at py4web instead
The framework settles the adoption question before an evaluator gets that far. It was created in 2007 for Python 2.7, moved to Python 3.9+ as of 2024, and no longer supports earlier versions. The README says not to use web2py for new projects and points to py4web, also known as web3py, while describing web2py 3.x as being in limited maintenance mode and naming 2.27.1 as the last version supporting Python 2.7.
The stated difference between the two projects is shared parts, not a shared codebase: web2py and py4web share the template language, pydal and rocket3, and the same design philosophy. That is the whole technical comparison on offer, and the README says nothing about what py4web drops, adds or renames. What it does offer is a chapter of py4web's documentation devoted to the move from web2py, plus a policy document at the root of this repository.
Maintenance signals disagree with each other, and the disagreement is worth stating plainly. The last push landed on 2026-09-14, a date recent enough to make the project look actively developed. Against that, the framework's own text calls 3.x limited maintenance and sends new work elsewhere. Commits still arrive, while the stated direction has not changed.
Where web2py is the wrong tool for a new deployment
web2py is the wrong choice in one common case: when the deliverable is a package that gets installed into an existing WSGI stack. A WSGI adapter exists in `handlers/`, so mounting is possible, yet the application still has to live as a directory under `applications/`, and the framework writes session, cache and upload files next to the code that uses them. That model suits a server you control end to end, and it does not suit a deployment where every service is versioned and installed on its own.
The terms themselves are a gap of a different kind: the README carries no licence section and no SPDX identifier, so the file named LICENSE.web2py.txt in the tree is the only place to read them, and nothing there explains how they sit with PyDAL in its separate repository.
The fit that remains is keeping an existing installation alive, and the first checks are concrete. Clone with `--recursive` so PyDAL is populated, read `versioning-and-support-policy.md` for the version policy, and look at whether the inherited code depends on the `welcome/` scaffold's directory layout before changing any model or controller.
Editorial conclusion
Take web2py only when an application already lives in its tree and the goal is keeping it running, not starting something new, because the project's own text points fresh work at py4web and calls 3.x limited maintenance. Teams whose deliverable is a wheel installed into an existing WSGI stack should skip it, since the framework writes session, cache and upload files next to the code. Verify two things before touching anything: clone with `--recursive` so PyDAL is present, then read `versioning-and-support-policy.md`, because the build writes 3.3.3 while the newest tag is v3.1.1.
Frequently asked questions
Does web2py need to be installed before it can run?
No. The installation notes say to unpack the archive and run `python web2py.py` from that directory, and the root Makefile prints that you do not need to make anything. The `install` target there exists to distribute the framework through pip, not to run it.
Why does cloning web2py require the --recursive flag?
The Database Abstraction Layer was decoupled into PyDAL in early 2015 and is registered in git as a submodule of the main repository, so a clone has to fetch it explicitly. For an existing checkout the equivalent one-time step is `git submodule update --init --recursive`.
Which Python versions does web2py support?
web2py was created in 2007 for Python 2.7, and as of 2024 it supports Python 3.9+ with earlier versions dropped. Version 2.27.1 is named as the last one supporting Python 2.7, and the 3.x line covers Python 3.9+.
Is web2py still a good choice for a new project?
The project's own note says not to use web2py for new projects and points to py4web instead, which it says shares the template language, pydal and rocket3. It also describes web2py 3.x as being in limited maintenance mode.
Where does web2py keep session, cache and upload files?
Inside each application directory as plain files: the stock `welcome/` scaffold carries `sessions/`, `cache/` and `uploads/` folders. The `clean` target of the root Makefile removes them with `rm -rf` across every app, together with IDE dotfiles under `applications/admin/`.
Official sources
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.
[](https://hysenlabs.com/projects/web2py-web2py)Was this useful?