Skip to content

Commit 94e3b85

Browse files
jpmckinneyclaude
andcommitted
Reword the requirements_base starter sentence #162
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_013mEwtfE72tX2eUWEHJb1Zs
1 parent 51329fc commit 94e3b85

5 files changed

Lines changed: 65 additions & 18 deletions

File tree

docs/docker/django.rst

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@ When using the ``gthreads`` worker class, a main thread `handles the heartbeat <
4646
Number of threads
4747
~~~~~~~~~~~~~~~~~
4848

49-
Ensure your code is thread safe. Notably, `psycopg2 cursors are not thread safe <https://www.psycopg.org/docs/cursor.html>`__, though this isn't a concern for typical usage of `Django <https://docs.djangoproject.com/en/5.2/ref/databases/>`__.
49+
Ensure your code is thread safe. Notably, `psycopg cursors are not thread safe <https://www.psycopg.org/psycopg3/docs/advanced/async.html>`__, though this isn't a concern for typical usage of `Django <https://docs.djangoproject.com/en/5.2/ref/databases/>`__.
5050

5151
`When using threads <https://docs.gunicorn.org/en/stable/design.html#how-many-threads>`__, the application is loaded by the worker and some memory is shared between its threads (thus consuming less memory than additional workers would).
5252

docs/docker/dockerfile.rst

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -96,7 +96,7 @@ Set ``server_tokens off;`` to prevent false positives from penetration tests (Ub
9696
System packages
9797
---------------
9898

99-
Before installing a system package, check whether it's included in a base image. For example, the ``psycopg2`` Python package `requires <https://www.psycopg.org/install/>`__ the ``libpq-dev`` system package. To check whether it's included, when using the `python:3.14 image <https://hub.docker.com/_/python>`__:
99+
Before installing a system package, check whether it's included in a base image. For example, the ``psycopg[c]`` Python package `requires <https://www.psycopg.org/psycopg3/docs/basic/install.html#local-installation>`__ the ``libpq-dev`` system package to build. To check whether it's included, when using the `python:3.14 image <https://hub.docker.com/_/python>`__:
100100

101101
#. Find the tag on the DockerHub page of the base image (the ``3.14`` tag is under *Shared Tags*)
102102
#. Click the link to view the Dockerfile

docs/python/preferences.rst

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -29,11 +29,11 @@ Command-line interface
2929
DataFrames
3030
`Polars <https://pola.rs>`__, unless end-users are unfamiliar (`pandas <https://pandas.pydata.org/docs/>`__).
3131
Object Relational Mapper (ORM)
32-
Django. If you don't need an ORM, use `psycopg2 <https://www.psycopg.org/docs/>`__. Do not use `SQLAlchemy <https://www.sqlalchemy.org/>`__, except with FastAPI or in low-level libraries with limited scope *where an ORM is needed*.
32+
Django. If you don't need an ORM, use `psycopg <https://www.psycopg.org/psycopg3/docs/>`__. Do not use `SQLAlchemy <https://www.sqlalchemy.org/>`__, except with FastAPI or in low-level libraries with limited scope *where an ORM is needed*.
3333

3434
.. note::
3535

36-
Use ``psycopg2`` in production, not ``psycopg2-binary``, `as recommended <https://www.psycopg.org/docs/install.html#psycopg-vs-psycopg-binary>`__. :ref:`See instructions<requirements-psycopg2>`.
36+
Use ``psycopg[c]`` in production and ``psycopg[binary]`` in development, `as recommended and preferred <https://www.psycopg.org/psycopg3/docs/basic/install.html#local-installation>`__. :ref:`See instructions<requirements-psycopg>`.
3737

3838
HTTP client
3939
`Requests <https://docs.python-requests.org/en/latest/>`__ for synchronous code and `niquests <https://niquests.readthedocs.io/en/latest/>`__ for asynchronous code, unless a framework uses another, like Scrapy (Twisted).

docs/python/requirements.rst

Lines changed: 55 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,10 @@ A common starter ``requirements.in`` for :doc:`django` is:
6363

6464
.. literalinclude:: ../../cookiecutter-django/{{cookiecutter.project_slug}}/requirements.in
6565

66+
Its shared requirements live in ``requirements_base.in``, which the starter splits out because it uses :ref:`psycopg<requirements-psycopg>`:
67+
68+
.. literalinclude:: ../../cookiecutter-django/{{cookiecutter.project_slug}}/requirements_base.in
69+
6670
A common starter ``requirements_dev.in`` is:
6771

6872
.. literalinclude:: ../../cookiecutter-django/{{cookiecutter.project_slug}}/requirements_dev.in
@@ -97,20 +101,63 @@ Add the requirement in alphabetical order to the appropriate ``.in`` file. Then,
97101

98102
:doc:`preferences`
99103

100-
.. _requirements-psycopg2:
104+
.. _requirements-psycopg:
105+
106+
psycopg
107+
~~~~~~~
108+
109+
Use `psycopg <https://www.psycopg.org/psycopg3/docs/>`__ (psycopg 3), not the older ``psycopg2``. It is a single package with extras: use ``psycopg[c]`` (compiled against the system ``libpq``) in production, and ``psycopg[binary]`` (a self-contained wheel) in development, keeping the ``psycopg`` version in sync.
110+
111+
Unlike ``psycopg2`` and ``psycopg2-binary``, the extras install different modules, so you can't add the binary build on top of the production lock. Instead, share a locked base across three files:
112+
113+
``requirements_base.in``
114+
The requirements shared by both environments (everything except psycopg).
115+
``requirements.in``
116+
``-r requirements_base.txt`` and ``psycopg[c]``.
117+
``requirements_dev.in``
118+
``-r requirements_base.txt``, ``psycopg[binary]`` and the development-only tools.
119+
120+
When you add or upgrade a requirement shared by both environments, edit ``requirements_base.in`` and compile base-first, so that the downstream locks pick up its versions:
121+
122+
.. code-block:: bash
123+
124+
uv pip compile requirements_base.in -o requirements_base.txt
125+
uv pip compile requirements.in -o requirements.txt
126+
uv pip compile requirements_dev.in -o requirements_dev.txt
127+
128+
Order the ``pip-compile`` pre-commit hooks the same way, and set ``files:`` patterns so that changing the base re-triggers the dependent locks:
129+
130+
.. code-block:: yaml
131+
:caption: .pre-commit-config.yaml
101132
102-
psycopg2
103-
~~~~~~~~
133+
- id: pip-compile
134+
name: pip-compile requirements_base.in
135+
args: [requirements_base.in, -o, requirements_base.txt]
136+
files: ^requirements_base\.(in|txt)$
137+
- id: pip-compile
138+
name: pip-compile requirements.in
139+
args: [requirements.in, -o, requirements.txt]
140+
files: ^requirements(_base)?\.(in|txt)$
141+
- id: pip-compile
142+
name: pip-compile requirements_dev.in
143+
args: [requirements_dev.in, -o, requirements_dev.txt]
144+
files: ^requirements(_base|_dev)?\.(in|txt)$
104145
105-
``psycopg2`` is `recommended <https://www.psycopg.org/docs/install.html#psycopg-vs-psycopg-binary>`__ for production. However, installing ``psycopg2`` for development can be difficult on operating systems like macOS. In that case, you can:
146+
Add ``requirements_base.in`` to the :ref:`requirements check<linting-ci>` in ``lint.yml``:
106147

107-
- Put ``psycopg2`` in ``requirements.in``
108-
- Put ``psycopg2-binary`` in ``requirements_dev.in``
109-
- Run: ``pip install psycopg2-binary``
148+
.. code-block:: yaml
149+
:caption: .github/workflows/lint.yml
150+
151+
with:
152+
standard-maintenance-scripts-files: requirements_base.in
110153
111154
.. note::
112155

113-
You **must** keep the locked versions of psycopg2 and psycopg2-binary in sync.
156+
When first adding ``requirements_base.txt``, compile it against the existing lock, so that the diff touches only psycopg and not unrelated packages:
157+
158+
.. code-block:: bash
159+
160+
uv pip compile -c requirements.txt requirements_base.in -o requirements_base.txt
114161
115162
Install requirements
116163
--------------------

docs/services/postgresql.rst

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -10,12 +10,12 @@ Connect to a database
1010

1111
Connect to the database using a connection string stored in the ``DATABASE_URL`` environment variable.
1212

13-
In Python, connect to the database using `dj-database-url <https://github.com/kennethreitz/dj-database-url#readme>`__ if using :doc:`Django<../python/django>`, or `psycopg2 <https://www.psycopg.org/docs/module.html#psycopg2.connect>`__ otherwise.
13+
In Python, connect to the database using `dj-database-url <https://github.com/kennethreitz/dj-database-url#readme>`__ if using :doc:`Django<../python/django>`, or `psycopg <https://www.psycopg.org/psycopg3/docs/api/connections.html#psycopg.Connection.connect>`__ otherwise.
1414

1515
To set the search path for a PostgreSQL connection, append to the connection string:
1616

1717
.. code-block:: none
18-
:caption: psycopg2
18+
:caption: psycopg
1919
2020
?options=-csearch_path%3Dmyschema,public
2121
@@ -79,9 +79,9 @@ Construct SQL statements
7979

8080
`GitLab SQL Style Guide <https://handbook.gitlab.com/handbook/enterprise-data/platform/sql-style-guide/>`__
8181

82-
Follow `best practices <https://www.psycopg.org/docs/usage.html#sql-injection>`__ to avoid accidental errors and `SQL injection <https://en.wikipedia.org/wiki/SQL_injection>`__. The code samples below use the psycopg2 Python package.
82+
Follow `best practices <https://www.psycopg.org/psycopg3/docs/basic/params.html>`__ to avoid accidental errors and `SQL injection <https://en.wikipedia.org/wiki/SQL_injection>`__. The code samples below use the psycopg Python package.
8383

84-
- `Pass parameters to SQL queries <https://www.psycopg.org/docs/usage.html#passing-parameters-to-sql-queries>`__, using the second argument to the ``execute`` method. This adapts the Python value's type (like ``bool``, ``int``, ``str``) to the correct SQL representation:
84+
- `Pass parameters to SQL queries <https://www.psycopg.org/psycopg3/docs/basic/params.html>`__, using the second argument to the ``execute`` method. This adapts the Python value's type (like ``bool``, ``int``, ``str``) to the correct SQL representation:
8585

8686
.. code-block:: python
8787
@@ -131,11 +131,11 @@ Follow `best practices <https://www.psycopg.org/docs/usage.html#sql-injection>`_
131131
SELECT * FROM record WHERE collection_id = %s AND ocid = %s
132132
""", (1, 1, 'ocds-213czf-1')) # AVOID
133133
134-
- If you are writing a query template in which you want to substitute column names or table names, use the ``format`` method and the ``SQL`` and ``Identifier`` classes (`documentation <https://www.psycopg.org/docs/sql.html>`__):
134+
- If you are writing a query template in which you want to substitute column names or table names, use the ``format`` method and the ``SQL`` and ``Identifier`` classes (`documentation <https://www.psycopg.org/psycopg3/docs/api/sql.html>`__):
135135

136136
.. code-block:: python
137137
138-
from psycopg2.sql import SQL, Identifier
138+
from psycopg.sql import SQL, Identifier
139139
140140
cur.execute(SQL("SELECT * FROM {table}").format(table=Identifier('collection')))
141141

0 commit comments

Comments
 (0)