This is a redistribution of the ONS dataset on Lower Tier Local Authority toUpper Tier Local Authority Lookup packaged for the Quality Lac Data project.Source: Office for National Statistics licensed under the Open Government Licence v.3.0
Project description
Quality LAC Data Reference - Postcodes
This is a redistribution of the ONS Postcode Directory shaped to be used in the Quality Lac Data project.
This repository contains PyPI distribution of a subset of this dataset as well as the scripts to generate them from source.
Source: Office for National Statistics licensed under the Open Government Licence v.3.0
Read more about this dataset here:
To keep distribution small, the data is stored in msgpack format with brotli compression. This provides efficient serialization without the security concerns and version coupling issues associated with pickle.
Regular updates
When a new postcode distribution is available, download it and add it to the source folder and at the same time delete the existing file from this location. There can only be one file in the source folder at a time.
After updating the postcode sources, regenerate the output files using the CLI command:
python -m qlacref_postcodes generate source/ONSPD_NOV_2025.zip
Or if you have the CLI extra installed:
postcodes generate source/ONSPD_NOV_2025.zip
This will regenerate the output files for each letter of the alphabet in the qlacref_postcodes directory.
Commit everything to GitHub. If ready to make a release, make sure to update the version in pyproject.toml, push to GitHub and then create a GitHub release. The GitHub Action will then create the distribution files and upload to PyPI.
Release naming should follow a pseudo-semantic versioning format:
<YEAR>.<MONTH>.<PATCH>. Alpha and beta releases can be flagged by appending
-alpha.<number> and -beta.<number>.
For example, the August 2021 release is named 2021.8 with the associated tag v2021.8.
Version Resolution
The package includes robust version resolution that works across different Python environments, including Pyodide/WASM. The version can be accessed programmatically:
import qlacref_postcodes
print(qlacref_postcodes.__version__)
The version resolver uses multiple fallback strategies to reliably detect the package version:
- Parses
pyproject.tomldirectly (most reliable for pre-releases) - Uses
importlib.metadata.version()(standard Python environments) - Iterates through installed distributions (works better in Pyodide/WASM)
- Falls back to "unknown" if all strategies fail
Development Versioning: For development branches, we recommend using .devX
suffixes (e.g., 2025.11.1.dev0, 2025.11.1.dev1). These are PEP 440 compliant
and work seamlessly with the version resolver. The .devX format clearly
indicates development versions while maintaining compatibility with package
management tools.
Example development versions:
2025.11.1.dev0- First development version for 2025.11.12025.11.1.dev1- Second development version2025.11.1-alpha.1- Alpha release (for testing before final release)2025.11.1-beta.1- Beta release
The version resolver returns the exact version string as written in pyproject.toml,
not the PEP 440 normalized version used in wheel filenames. This ensures consistent
version reporting across all environments.
Development Environment
This project includes a VS Code devcontainer configuration for a consistent development environment.
Devcontainer Setup
The .devcontainer/ directory contains:
- Dockerfile: Based on Python 3.8 with conservative build tools (for compatibility with old numpy/pandas versions)
- devcontainer.json: VS Code configuration for the container
The devcontainer:
- Uses Python 3.8 (Debian Bullseye)
- Installs Poetry 1.8.5
- Configures Poetry to keep the virtual environment in the project directory (
virtualenvs.in-project true) - Automatically runs
poetry installwhen the container is created - Includes VS Code Python extensions (Python and Pylance)
To use the devcontainer:
- Open the project in VS Code
- When prompted, click "Reopen in Container" (or use Command Palette: "Dev Containers: Reopen in Container")
- The container will build and install dependencies automatically
The devcontainer uses conservative versions of build tools (pip<24, setuptools<60, wheel<0.39, Cython<3) to ensure compatibility with older numpy/pandas versions required for Pyodide compatibility.
Command Line Interface (CLI)
The package includes an optional CLI for generating postcode files and searching postcodes. The CLI is available as an optional extra to keep the core package lightweight.
Installation
To install with CLI support:
pip install quality-lac-data-ref-postcodes[cli]
Or with Poetry:
poetry install --extras cli
Usage
Once installed, you can use the CLI via:
python -m qlacref_postcodes <command>
Or if installed as a script:
postcodes <command>
Available Commands
generate
Convert a source ZIP file to msgpack.br format files:
postcodes generate source/ONSPD_NOV_2025.zip [output_dir]
input_file: Path to the source ZIP file (required)output_dir: Output directory (defaults to package directory)
This command:
- Extracts and parses the CSV file from the ZIP
- Maps source columns to expected format (pcd7→pcd, east1m→oseast1m, etc.)
- Splits postcodes by first letter
- Saves each letter's data as a compressed msgpack file
to_parquet
Convert a source ZIP file to Parquet format:
postcodes to_parquet source/ONSPD_NOV_2025.zip [output_file]
input_file: Path to the source ZIP file (required)output_file: Output file path (defaults to input filename with .parquet extension)
search
Search for postcodes interactively with formatted table output:
postcodes search "SW1A" [--data-dir /path/to/data]
partial_postcode: Partial postcode to search for (required)--data-dir: Optional path to data directory (defaults to package directory)
The search command displays results in a formatted table with columns for postcode, easting, northing, and local authority code.
Example Workflow
# Generate postcode files from source
postcodes generate source/ONSPD_NOV_2025.zip
# Search for postcodes
postcodes search "SW1A"
# Convert to Parquet for other tools
postcodes to_parquet source/ONSPD_NOV_2025.zip output.parquet
Testing in Pyodide/WASM
This project includes examples for testing the library in Pyodide (WebAssembly) environments. Pyodide allows Python code to run in browsers and Node.js using WebAssembly.
Why Test in Pyodide?
Testing in Pyodide ensures:
- WASM Compatibility: Your code works in WebAssembly environments
- Browser Compatibility: If you plan to run Python in browsers
- Cross-Platform: Verify your code works in JavaScript runtimes
- Package Compatibility: Ensure dependencies work in Pyodide
Browser Example
The browser example (examples/pyodide-v0.22.1-browser/) demonstrates testing the library in a web browser using Pyodide.
Important: The browser example requires the wheel to be built first before testing.
Setup
-
Build the wheel:
poetry buildThis creates the wheel file in the
dist/directory. -
Start the Flask server:
poetry run serve
Or run directly:
python examples/pyodide-v0.22.1-browser/app.py
The server will:
- Check if the wheel exists in
dist/before starting (provides helpful error if missing) - Start on
http://localhost:8123(or the port specified byPORTenvironment variable) - Serve an HTML page that loads Pyodide from CDN
- Automatically install the built wheel in the browser environment
Usage
- Open your browser and navigate to
http://localhost:8123(or the port shown in the server output) - Wait for Pyodide to load (may take a moment on first load)
- Enter Python code in the left panel
- Click "Run Code" or press Ctrl+Enter
- View output in the right panel
The page includes example scripts:
- Basic: Check platform and Python version
- Columns: Inspect the dataframe structure
- Load: Load postcodes for a specific letter
- Query: Query and display loaded postcode data
Note: The browser example automatically installs required packages (msgpack, brotli) via Pyodide's package system.
Node.js Example
The Node.js example (examples/pyodide-v0.22.1-node/) uses Vitest to test the library in Pyodide/WASM within a Node.js environment.
Setup
-
Navigate to the example directory:
cd examples/pyodide-v0.22.1-node
-
Install dependencies:
pnpm install
Running Tests
# Run all tests
pnpm test
# Run specific test file
pnpm test test.test.js
# Run tests in watch mode
pnpm test --watch
How It Works
The Node.js example:
- Uses Vitest as the test runner
- Creates a Pyodide instance configured for Node.js
- Copies the
qlacref_postcodesmodule to Pyodide's virtual filesystem - Loads required packages (pandas, msgpack, brotli) via Pyodide's package system
- Verifies WASM execution by checking
sys.platform == "emscripten"
The test helper (pyodide-test-helper.js) provides utilities:
createPyodideInstance(): Creates a configured Pyodide instancerunPythonCode(): Executes Python codeloadPythonFile(): Loads Python files from disksetupQlacrefPostcodes(): Sets up the module in Pyodide with proper configuration
Testing Your Code
The example includes tests that:
- Verify WASM execution environment
- Test module import and instantiation
- Test dataframe access and column structure
- Test loading postcodes for specific letters
- Test querying loaded postcode data
You can modify the tests in test-qlacref-postcodes.test.js to test your specific use cases.
Key Differences
- Browser Example: Interactive testing in a web browser, requires built wheel, uses Flask server
- Node.js Example: Automated testing with Vitest, can test source code directly, runs in Node.js environment
Both examples verify that code is running in WASM by checking sys.platform == "emscripten".
Troubleshooting
Browser Example
Pyodide fails to load:
- Check your internet connection (Pyodide loads from CDN)
- Check browser console for errors
- Try refreshing the page
Wheel installation fails:
- Ensure the wheel file exists in
dist/ - Check that the Flask server can access the
dist/directory - Verify the wheel path in
index.htmlmatches your actual wheel filename
Module import errors:
- Make sure all dependencies (pandas, msgpack, brotli) are loading correctly
- Check the browser console for detailed error messages
Node.js Example
Module Not Found Errors:
- Check that
indexURLis set correctly in the test helper - Verify the Pyodide package path is correct
- Ensure WASM files are accessible
Package Loading Issues:
- Check if the package is available in Pyodide
- Verify package version compatibility
- Check network connectivity (first load downloads from CDN)
Path Issues:
- Ensure
indexURLis set to a directory path (not a URL) - Use absolute paths when possible
- Check Vitest configuration
Resources
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file quality_lac_data_ref_postcodes-2026.1.1.tar.gz.
File metadata
- Download URL: quality_lac_data_ref_postcodes-2026.1.1.tar.gz
- Upload date:
- Size: 8.3 MB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: poetry/2.3.2 CPython/3.12.12 Linux/6.8.0-1030-azure
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
685f6a06034f300ad81718b1390700b6674a803f25556b64a8ea81cc72bfa034
|
|
| MD5 |
495898968fe7efa867e14dc5414a3838
|
|
| BLAKE2b-256 |
4ff7cb2a05c250fbfd72fd741d032c85a83077caac353eb35f0dc2f447721d37
|
File details
Details for the file quality_lac_data_ref_postcodes-2026.1.1-py3-none-any.whl.
File metadata
- Download URL: quality_lac_data_ref_postcodes-2026.1.1-py3-none-any.whl
- Upload date:
- Size: 8.3 MB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: poetry/2.3.2 CPython/3.12.12 Linux/6.8.0-1030-azure
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
3ee7ba7bf8de1a8164511bee936acb851662e76bd52dcf331c8882e02c553d06
|
|
| MD5 |
1dfad36b010c8f3c12759829ee594e8a
|
|
| BLAKE2b-256 |
bda6aa091439d7a357e36ed6c52c127cffabf64d2bec62bdb1eea6cb3826956c
|