# python-gitlab's own docs still point at v3.2.0 image tags

> The Python client for GitLab's v4 REST and GraphQL APIs is current, with v8.6.0 released on 2026-09-28, yet its README carries versioned image tags from v3.2.0, no token example, and a container that leaves out the GraphQL extra.

**python-gitlab/python-gitlab** — A python wrapper for the GitLab API.

- Repository: https://github.com/python-gitlab/python-gitlab
- Website: https://python-gitlab.readthedocs.io
- Stars: 2,476 · Forks: 676
- Language: Python
- License: NOASSERTION
- Published: 2026-09-28 · Updated: 2026-09-28 · Language: en
- Canonical page: https://hysenlabs.com/projects/python-gitlab-python-gitlab

## Six registry tags are named, and three of them are pinned to v3.2.0

The documentation names six image tags on the GitLab registry: `registry.gitlab.com/python-gitlab/python-gitlab:latest` as the alpine alias, plus `:alpine`, `:slim-bullseye`, `:v3.2.0`, `:v3.2.0-alpine` and `:v3.2.0-slim-bullseye`. Three of the six are pinned to v3.2.0, while the release line has moved through v8.4.0 on 2026-05-28, v8.5.0 on 2026-07-28 and v8.6.0 on 2026-09-28. Anyone who copies the versioned form gets a client from five major versions back, and nothing on the page warns about it. The floating tags do track the current line, but the default tag is the alpine alias, so `:latest` is an alias for `:alpine` rather than an unversioned build in its own right. The same gap shows up in the compatibility sentence, which fixes Python 3.10 or newer to 7.0.0 while the project ships 8.x.

## The image hand installs PyYaml and never installs the GraphQL extra

The published image is not the optional dependency install. The Dockerfile builds a wheel in a builder stage:

```dockerfile
ARG PYTHON_FLAVOR=alpine
FROM python:3.12-${PYTHON_FLAVOR} AS build
WORKDIR /opt/python-gitlab
COPY . .
RUN pip install --no-cache-dir build && python -m build --wheel
```

then copies it into a runtime stage and installs PyYaml by hand before the wheel itself:

```dockerfile
FROM python:3.12-${PYTHON_FLAVOR}
WORKDIR /opt/python-gitlab
COPY --from=build /opt/python-gitlab/dist dist/
RUN pip install --no-cache-dir PyYaml
```

So PyYaml lands in the container outside the dependency graph, duplicating the `yaml` extra declared as `PyYaml>=6.0.1`, while `gql[httpx]>=3.5.0,<5` and `argcomplete>=1.10.0,<4` are never installed at all. A `docker run` of that image can call REST endpoints through the CLI but has no GraphQL client to import and no shell completion. A plain pip install has the same gap: only `requests>=2.32.0` and `requests-toolbelt>=1.0.0` arrive. The flavor is a build argument rather than a choice at run time, and its default is the smaller alpine base:

```console
$ docker build -t python-gitlab:latest --build-arg PYTHON_FLAVOR=slim-bullseye .
```

That produces the Debian slim variant the docs suggest for CI jobs that need a full bash shell, which is also the variant to reach for when the alpine default trips over something. The base image is pinned to Python 3.12 even though the package requires 3.10.0 or newer and classifies 3.10 through 3.14.

## ENTRYPOINT is the CLI, which is why the CI example blanks it out

The Dockerfile ends with `ENTRYPOINT ["gitlab"]` and `CMD ["--version"]`, so every argument after the image name is handed to the CLI. That is why the GitLab CI snippet has to blank the entrypoint:

```yaml
Job Name:
   image:
      name: registry.gitlab.com/python-gitlab/python-gitlab:latest
      entrypoint: [""]
   before_script:
      gitlab --version
   script:
      gitlab <command>
```

With the entrypoint left in place, a `before_script` line of `gitlab --version` reaches the container as `gitlab gitlab --version`, because the runner passes the whole script line to the entrypoint as one argument. Blank the entrypoint and `gitlab <command>` in `script` behaves exactly as written. The default command is `CMD ["--version"]`, so a bare `docker run` prints the version and exits instead of dropping into anything interactive. The same argument passing is what makes the anonymous example work, with `--id` taking a full namespace and project path rather than a numeric ID:

```console
$ docker run -it --rm registry.gitlab.com/python-gitlab/python-gitlab:latest project get --id gitlab-org/gitlab
```

That command is documented as running against GitLab.com without authentication, which leaves the credential question for the reader.

## One install line, two git hosts, and no token anywhere on the page

Stable installs are one command, and the development version is installable from both hosts the project mirrors to:

```console
$ pip install --upgrade python-gitlab
$ pip install git+https://github.com/python-gitlab/python-gitlab.git
$ pip install git+https://gitlab.com/python-gitlab/python-gitlab.git
```

What the page never shows is how authentication is configured. Persistent requests sessions for authentication are listed as a feature, and merging configuration from files, environment variables and arguments is listed as another, yet the only configuration artifact named anywhere is a file mounted to `/etc/python-gitlab.cfg`:

```console
$ docker run -it --rm -v /path/to/python-gitlab.cfg:/etc/python-gitlab.cfg registry.gitlab.com/python-gitlab/python-gitlab:latest <command> ...
```

The path is given, the file contents are not, and the precedence when one value appears in more than one of the three sources is not stated either. That gap is the first thing to close before writing anything against a private instance, and the answer is not on this page: full CLI and API documentation sits on readthedocs, at the stable URL the project links to as its homepage. Anything that depends on knowing which source wins belongs in that documentation, not in a wrapper script you write from the README alone.

## Retries, rate limits and pagination arrive as promises, not switches

Several advertised behaviours are automatic and offered with no option attached: smart retries on network and server errors with rate-limit handling, flexible handling of paginated responses including lazy iterators, automatic URL encoding of paths and parameters, and automatic conversion of some complex data structures into API attribute types. No flag for any of them appears on the page, so a caller cannot tell from this documentation whether a failed request was retried, how many times, or on which status codes, and cannot turn the behaviour off for a job that needs to fail loudly. Laziness has the same problem: the iterator contract that decides how many pages a listing pulls and when it stops is not described. One bullet is a pass-through policy with a real consequence. Arbitrary parameters can be sent to the GitLab API by following the server's own documentation on what is available, which means an unknown or misspelled parameter is caught by the server rather than by the client, and the error you get back describes the API, not the wrapper. Reachability of new endpoints is arranged the same way, through lower level API methods, so endpoint shape is the server's to change.

## Six requirements files sit beside a two dependency package

The tree carries `requirements.txt`, `requirements-docker.txt`, `requirements-docs.txt`, `requirements-lint.txt`, `requirements-precommit.txt` and `requirements-test.txt`. The published runtime surface is far smaller than that list suggests, since pyproject requires only `requests>=2.32.0` and `requests-toolbelt>=1.0.0`. The flat file pins everything exactly, including the GraphQL stack that only the optional extra needs:

```text
gql==4.0.0
httpx==0.28.1
requests==2.34.2
requests-toolbelt==1.0.0
```

So the same dependency is expressed twice in two different ways: an open range for consumers, an exact pin for development, with `gql[httpx]>=3.5.0,<5` sitting inside a `gql==4.0.0` pin. Pinning both ends is what makes a test run reproducible, and it is also how a resolver can end up validating against versions the package never declared. Around these six files sit `.renovaterc.json`, `.pre-commit-config.yaml` and `.git-blame-ignore-revs`, which is the visible machinery behind those pins.

## The license value, the classifier and the root file do not line up

Three sources fail to agree about licensing. The license value published for the repository itself is an unasserted marker rather than a license name, while pyproject declares `license = {text = "LGPL-3.0-or-later"}` and adds the classifier `License :: OSI Approved :: GNU Lesser General Public License v3 (LGPLv3)`. The file at the repository root is `COPYING`, not `LICENSE`, and the license badge in the README points at `blob/main/COPYING`. Which of those governs a downstream install is not settled on this page, and an LGPL label on a client library is exactly the kind of term an organization checks before vendoring, so read `COPYING` and the packaged metadata yourself instead of trusting the badge. Smaller packaging details sit next to it: `AUTHORS`, `MANIFEST.in`, `CODE_OF_CONDUCT.md` and `SECURITY.md` are all present at the root, and the `[project.urls]` table in pyproject stops after its `Documentation` label. The entry point itself is declared normally, as `gitlab = "gitlab.cli:main"`.

## Releases land about every two months from a dynamic version

`pyproject.toml` sets `dynamic = ["version"]`, so no version string is written into the packaging metadata at all and the release number comes from the code. The recent tags are v8.4.0 on 2026-05-28, v8.5.0 on 2026-07-28 and v8.6.0 on 2026-09-28, an interval a little over two months, with the last push to `main` dated 2026-10-02. Documentation is built with tox, and the tox file also governs the environment those commands run in:

```console
pip install tox
tox -e docs
```

Channels are split three ways. Bug reports are requested on the GitHub issue tracker, a Gitter lobby is offered for questions simple enough to answer without opening an issue, and the development version is installable from a GitLab.com mirror that carries its own registry. A workaround found in one of those places is not automatically true of the other two, which is a small tax on anyone trying to follow the project across hosts.

## Conclusion

Take the client, not the README, as the source of truth here. The code is current and the API surface is broad, but the page you would install from is five major versions behind on image tags, shows no token configuration at all, and ships a container whose only extra is YAML. Before committing, pin your own image tag instead of copying the versioned examples, decide whether you need the GraphQL extra and install it yourself, and read COPYING plus the packaged license metadata rather than trusting the badge. The 65% refresh floor is on the prose, not the code: nothing here should stop you from pinning a specific release and running the REST client in anger.

## FAQ

### What is python-gitlab?

A Python package providing access to the GitLab APIs: a client for GitLab's v4 REST API, synchronous and asynchronous GraphQL clients, and a CLI tool named gitlab that wraps REST API endpoints. It requires Python 3.10 or newer as of version 7.0.0.

### How do you install python-gitlab?

Use pip to get the latest stable version with pip install --upgrade python-gitlab. The development version is installed straight from git, from github.com/python-gitlab/python-gitlab.git or from the GitLab.com mirror of the same path, and there are container images on the GitLab registry.

### Is there a GitLab API for Python?

Yes, and this package is one of them: a client for GitLab's v4 REST API plus synchronous and asynchronous GraphQL clients. The GraphQL support comes from the optional gql[httpx] extra, which the published Docker image does not install because its Dockerfile only adds PyYaml.

## Sources

- [Issues](https://github.com/python-gitlab/python-gitlab/issues)
- [Project website](https://python-gitlab.readthedocs.io)
- [python-gitlab/python-gitlab on GitHub](https://github.com/python-gitlab/python-gitlab)
- [README](https://github.com/python-gitlab/python-gitlab/blob/main/README.md)
- [Releases](https://github.com/python-gitlab/python-gitlab/releases)

---

Hysen Labs editorial analysis, written from the project's own repository and release notes. Cite the canonical page: https://hysenlabs.com/projects/python-gitlab-python-gitlab
