Skip to content

Commit f028403

Browse files
A less opinionated version
* A less opinionated version * Updates from review
1 parent c718c95 commit f028403

2 files changed

Lines changed: 158 additions & 0 deletions

File tree

docs/README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ These are the standards that must be followed for all software development under
3636
- [Logging standards](./standards/logging_standards.md)
3737
- [Mobile application standards](./standards/mobile_app_standards.md)
3838
- [Node.js standards](./standards/node_standards.md)
39+
- [Python coding standards](./standards/python_coding_standards.md)
3940
- [UiPath standards](./standards/uipath_standards.md)
4041
- [Quality assurance and test standards](./standards/quality_assurance_standards.md)
4142
- [Security standards](./standards/security_standards.md)
Lines changed: 157 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,157 @@
1+
# Python Standards
2+
3+
This document defines the internal development standards for building backend services in Python ([FastAPI](https://fastapi.tiangolo.com/tutorial/)) within Defra. It adheres strictly to the [UK Government Digital Service (GDS) Python style guide](https://gds-way.digital.cabinet-office.gov.uk/manuals/programming-languages/python/python.html) and [PEP 8](https://peps.python.org/pep-0008/), ensuring code is clear, consistent, and maintainable across all AI services (using tools like [LangChain](https://python.langchain.com/docs/introduction/), [LangGraph](https://langchain-ai.github.io/langgraph/tutorials/introduction/), etc.). All developers must follow these conventions when writing Python code for backend services.
4+
5+
**Note:** Python should **ONLY** be used for creating backend services related to AI or data science. For frontend services, use Node.js using the following [Node.js](https://defra.github.io/software-development-standards/standards/node_standards/) and [Javascript](https://defra.github.io/software-development-standards/standards/javascript_standards/) Defra standards.
6+
7+
## Environments
8+
These standards advises the use of uv to manage different versions of Python you have installed.
9+
10+
To create virtual environments call `uv venv -p python3.13` from your project root directory. This will create a virtual environment with that specific python version in a folder called `.venv`. This folder should be excluded in your `.gitignore` file. For more information see [Python virtual environment primer](https://realpython.com/python-virtual-environments-a-primer/)
11+
12+
## Linting
13+
14+
### Ruff
15+
These standards advises the use of the [Ruff](https://docs.astral.sh/ruff/) command line checker as an all in one formatter, linter, codestyle and complexity checker.
16+
17+
18+
## Function Annotations and Typing
19+
20+
- All public functions must include type annotations for parameters and return types.
21+
- Use standard [PEP 484](https://peps.python.org/pep-0484/) typing syntax.
22+
- Function definitions should format parameters and return types as follows:
23+
24+
```python
25+
def get_item(item_id: int, detail: bool = False) -> dict[str, str]:
26+
...
27+
```
28+
- Annotate variables where the type is not immediately clear:
29+
30+
```python
31+
items: list[str] = []
32+
```
33+
- Optional types should be annotated with | None or Optional from typing:
34+
```python
35+
def get_user(user_id: int | None = None) -> dict[str, str] | None:
36+
...
37+
```
38+
39+
- There should be no spaces before the colon and exactly one space after.
40+
41+
```python
42+
def get_mapping() -> dict[str, int]:
43+
return {'a': 1, 'b': 2}
44+
```
45+
46+
## Naming Conventions
47+
48+
- Variables and functions: Use snake_case.
49+
- Classes: Use PascalCase.
50+
- Constants: Use UPPER_CASE.
51+
52+
Example:
53+
```python
54+
MAX_RETRIES = 3
55+
56+
def calculate_total(items: list[int]) -> int:
57+
return sum(items)
58+
59+
class DataProcessor:
60+
pass
61+
- Private members should start with a single underscore (_).
62+
- Exception class names should end in Error.
63+
64+
```python
65+
class ValidationError(Exception):
66+
pass
67+
```
68+
69+
## Import Style
70+
71+
- Imports must be grouped in the following order:
72+
73+
1. Standard library imports
74+
75+
2. Third-party imports
76+
77+
3. Local application imports
78+
79+
- Each group must be separated by one blank line.
80+
81+
Example:
82+
```python
83+
import os
84+
import sys
85+
86+
from fastapi import FastAPI
87+
import requests
88+
89+
from app.models import User
90+
from app.services import user_service
91+
```
92+
93+
- Imports should be one per line.
94+
95+
- Absolute imports should be used.
96+
97+
- Wildcard imports (`from module import *`) should not be used.
98+
99+
## Error Handling
100+
101+
- Specific exceptions should be caught rather than using a bare `except:`.
102+
103+
- When raising exceptions, include an informative error message.
104+
105+
- Exceptions that represent a domain-specific error should subclass `Exception` and be suffixed with `Error`.
106+
107+
Example:
108+
```python
109+
from fastapi import HTTPException
110+
111+
def fetch_data(url: str) -> str:
112+
try:
113+
response = requests.get(url)
114+
response.raise_for_status()
115+
except requests.RequestException as exc:
116+
raise HTTPException(status_code=500, detail=str(exc))
117+
return response.text
118+
```
119+
120+
## File and Module Structure
121+
122+
- Modules and packages must use short, all-lowercase names. Underscores can be used if necessary.
123+
124+
- Each module must have a single, clear responsibility.
125+
126+
Example project structure:
127+
```
128+
129+
app/
130+
├── main.py
131+
├── api/
132+
│ ├── users.py
133+
│ └── items.py
134+
├── models/
135+
│ └── user.py
136+
├── services/
137+
│ └── user_service.py
138+
├── utils/
139+
│ └── helpers.py
140+
├── config.py
141+
142+
```
143+
## Constants and Configuration
144+
145+
- Constants must be defined using UPPER_CASE.
146+
147+
- Configuration values should be loaded from environment variables where possible.
148+
149+
Example:
150+
```python
151+
import os
152+
153+
MAX_RETRIES = 5
154+
DATABASE_URL = os.getenv("DATABASE_URL", "sqlite:///./test.db")
155+
```
156+
157+
- Hard-coded configuration values should be avoided.

0 commit comments

Comments
 (0)