Skip to content

Commit b452d0b

Browse files
committed
Added support for Python 3.10, 3.11, and 3.12
1 parent 54b15ca commit b452d0b

File tree

10 files changed

+151
-62
lines changed

10 files changed

+151
-62
lines changed

.github/workflows/build.yml

Lines changed: 14 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -16,27 +16,34 @@ jobs:
1616
matrix:
1717
config:
1818
- { os: windows-latest, py: "3.9" }
19+
- { os: windows-latest, py: "3.10" }
20+
- { os: windows-latest, py: "3.11" }
21+
- { os: windows-latest, py: "3.12" }
1922
- { os: macOS-latest, py: "3.9" }
20-
- { os: ubuntu-latest, py: "3.6" }
23+
- { os: macOS-latest, py: "3.10" }
24+
- { os: macOS-latest, py: "3.11" }
25+
- { os: macOS-latest, py: "3.12" }
2126
- { os: ubuntu-latest, py: "3.7" }
2227
- { os: ubuntu-latest, py: "3.8" }
2328
- { os: ubuntu-latest, py: "3.9" }
29+
- { os: ubuntu-latest, py: "3.10" }
30+
- { os: ubuntu-latest, py: "3.11" }
31+
- { os: ubuntu-latest, py: "3.12" }
2432

2533
env:
2634
SDKROOT: /Library/Developer/CommandLineTools/SDKs/MacOSX.sdk
2735
steps:
2836
- name: CHECKOUT CODE
29-
uses: actions/checkout@v2
37+
uses: actions/checkout@v4
3038
- name: SETUP PYTHON
31-
uses: actions/setup-python@v1
39+
uses: actions/setup-python@v5
3240
with:
3341
python-version: ${{ matrix.config.py }}
3442
- name: Install dependencies
3543
run: |
3644
python -m pip install --upgrade pip
37-
pip install --user --no-cache-dir Cython
38-
pip install --user -r requirements.txt
39-
pip install --user -r requirements_dev.txt
45+
pip install -r requirements.txt
46+
pip install -r requirements_dev.txt
4047
- name: PKG-TEST
4148
run: |
42-
python -m unittest discover tests/
49+
python -m pytest tests/ -v

.github/workflows/docs.yml

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -7,15 +7,14 @@ jobs:
77
deploy:
88
runs-on: ubuntu-latest
99
steps:
10-
- uses: actions/checkout@v2
11-
- uses: actions/setup-python@v2
10+
- uses: actions/checkout@v4
11+
- uses: actions/setup-python@v5
1212
with:
13-
python-version: 3.6
13+
python-version: '3.11'
1414
- name: Install dependencies
1515
run: |
1616
python -m pip install --upgrade pip
17-
pip install --user --no-cache-dir Cython
18-
pip install --user -r requirements.txt
17+
pip install -r requirements.txt
1918
2019
- run: python -m pip install --upgrade pip
2120
- run: pip install mkdocs-material mkdocstrings[python] mkdocs-git-revision-date-plugin mkdocs-jupyter ipykernel

.github/workflows/pypi.yml

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -13,19 +13,19 @@ jobs:
1313
runs-on: ubuntu-latest
1414

1515
steps:
16-
- uses: actions/checkout@v2
16+
- uses: actions/checkout@v4
1717
- name: Set up Python
18-
uses: actions/setup-python@v2
18+
uses: actions/setup-python@v5
1919
with:
20-
python-version: '3.x'
20+
python-version: '3.11'
2121
- name: Install dependencies
2222
run: |
2323
python -m pip install --upgrade pip
24-
pip install setuptools wheel twine
24+
pip install setuptools wheel twine build
2525
- name: Build and publish
2626
env:
27-
TWINE_USERNAME: ${{ secrets.PYPI_USERS }}
27+
TWINE_USERNAME: ${{ secrets.PYPI_USERNAME }}
2828
TWINE_PASSWORD: ${{ secrets.PYPI_PASSWORD }}
2929
run: |
30-
python setup.py sdist bdist_wheel
30+
python -m build
3131
twine upload dist/*

docs/changelog.md

Lines changed: 14 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,20 @@
11
# Changelog
22

3-
## v0.0.1 - Date
3+
## v1.0.3 - 2025-01-27
44

5-
**Improvement**:
5+
**Improvements**:
66

7-
- TBD
7+
- Updated Python version support to include Python 3.10, 3.11, and 3.12
8+
- Updated minimum Python version requirement to 3.7 (dropped support for Python 3.6)
9+
- Changed development status from Pre-Alpha to Beta
10+
- Fixed pytest configuration warnings
11+
- Updated dependencies and development tools
812

9-
**New Features**:
13+
**Maintenance**:
1014

11-
- TBD
15+
- Package maintenance and modernization
16+
- All tests passing
17+
18+
## v1.0.2 - Previous Release
19+
20+
Initial stable release with UK postcode validation and formatting functionality.

ideal_ukpostcode/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
__author__ = """AMAR DESAI"""
44
__email__ = 'amardesai.bgm@gmail.com'
5-
__version__ = '1.0.2'
5+
__version__ = '1.0.3'
66

77

88
from ideal_ukpostcode.pc_validate import validate

ideal_ukpostcode/pc_format.py

Lines changed: 83 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -1,46 +1,101 @@
11
import re
2+
from typing import Union
23

34
from ideal_ukpostcode.exceptions import InvalidArea, InvalidDistrict, InvalidSector, InvalidUnit
45

56

6-
def validate_areacode(area):
7-
# Validate a uk postcode area
8-
# input = The postcode area is either one or two characters long and is alphabetical.
9-
# return = A boolean result with whether the postcode area is valid or not
10-
11-
result = bool(re.match("[A-Z]{1,2}$", area))
7+
def validate_areacode(area: Union[str, None]) -> bool:
8+
"""
9+
Validate a UK postcode area.
10+
11+
The postcode area is either one or two characters long and is alphabetical.
12+
13+
Args:
14+
area: The postcode area to validate
15+
16+
Returns:
17+
True if the postcode area is valid, False otherwise
18+
"""
19+
if not area or not isinstance(area, str):
20+
return False
21+
result = bool(re.match(r"^[A-Z]{1,2}$", area))
1222
return result
1323

1424

15-
def validate_districtcode(district):
16-
# Validate a uk postcode district
17-
# input = The postcode district is one digit, two digits or a digit followed by a letter.
18-
# return = A boolean result with whether the postcode area is valid or not
19-
20-
result = bool(re.match("[0-9][A-Z0-9]?|ASCN|STHL|TDCU|BBND|[BFS]IQQ|PCRN|TKCA", district))
25+
def validate_districtcode(district: Union[str, None]) -> bool:
26+
"""
27+
Validate a UK postcode district.
28+
29+
The postcode district is one digit, two digits or a digit followed by a letter.
30+
31+
Args:
32+
district: The postcode district to validate
33+
34+
Returns:
35+
True if the postcode district is valid, False otherwise
36+
"""
37+
if not district or not isinstance(district, str):
38+
return False
39+
result = bool(re.match(r"^([0-9][A-Z0-9]?|ASCN|STHL|TDCU|BBND|[BFS]IQQ|PCRN|TKCA)$", district))
2140
return result
2241

2342

24-
def validate_sectorcode(sector):
25-
# Validate a uk postcode sector
26-
# input = The postcode sector is made up of a single digit
27-
# return = A boolean result with whether the postcode sector is valid or not
28-
29-
result = bool(re.match(r"\d$", sector))
43+
def validate_sectorcode(sector: Union[str, int, None]) -> bool:
44+
"""
45+
Validate a UK postcode sector.
46+
47+
The postcode sector is made up of a single digit.
48+
49+
Args:
50+
sector: The postcode sector to validate
51+
52+
Returns:
53+
True if the postcode sector is valid, False otherwise
54+
"""
55+
if sector is None:
56+
return False
57+
result = bool(re.match(r"^\d$", str(sector)))
3058
return result
3159

3260

33-
def validate_unitcode(unit):
34-
# Validate a uk postcode unit
35-
# input = The postcode unit is two characters
36-
# return = A boolean result with whether the postcode sector is valid or not
37-
38-
result = bool(re.match("[A-Z]{2}$", unit))
61+
def validate_unitcode(unit: Union[str, None]) -> bool:
62+
"""
63+
Validate a UK postcode unit.
64+
65+
The postcode unit is two characters.
66+
67+
Args:
68+
unit: The postcode unit to validate
69+
70+
Returns:
71+
True if the postcode unit is valid, False otherwise
72+
"""
73+
if not unit or not isinstance(unit, str):
74+
return False
75+
result = bool(re.match(r"^[A-Z]{2}$", unit))
3976
return result
4077

4178

42-
def format(area, district, sector, unit):
43-
79+
def format(area: Union[str, None], district: Union[str, None],
80+
sector: Union[str, int, None], unit: Union[str, None]) -> str:
81+
"""
82+
Format UK postcode components into a standard postcode string.
83+
84+
Args:
85+
area: The postcode area (1-2 alphabetical characters)
86+
district: The postcode district
87+
sector: The postcode sector (single digit)
88+
unit: The postcode unit (2 alphabetical characters)
89+
90+
Returns:
91+
Formatted postcode string (e.g., "EC1A 1BB")
92+
93+
Raises:
94+
InvalidArea: If the area code is invalid
95+
InvalidDistrict: If the district code is invalid
96+
InvalidSector: If the sector code is invalid
97+
InvalidUnit: If the unit code is invalid
98+
"""
4499
if not validate_areacode(str(area)):
45100
raise InvalidArea
46101
if not validate_districtcode(str(district)):
@@ -50,7 +105,7 @@ def format(area, district, sector, unit):
50105
if not validate_unitcode(str(unit)):
51106
raise InvalidUnit
52107

53-
outward_code = area + district
54-
inward_code = str(sector) + unit
108+
outward_code = str(area) + str(district)
109+
inward_code = str(sector) + str(unit)
55110

56111
return outward_code + " " + inward_code

ideal_ukpostcode/pc_validate.py

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,23 @@
11
import re
2+
from typing import Union
23

34

4-
def validate(postcode):
5+
def validate(postcode: Union[str, None]) -> bool:
6+
"""
7+
Validate a UK postcode.
8+
9+
Args:
10+
postcode: The postcode string to validate
11+
12+
Returns:
13+
True if the postcode is valid, False otherwise
14+
"""
15+
if not postcode or not isinstance(postcode, str):
16+
return False
17+
518
result = bool(
619
re.match(
7-
"^(([A-Z]{1,2}[0-9][A-Z0-9]?|ASCN|STHL|TDCU|BBND|[BFS]IQQ|PCRN|TKCA) ?[0-9][A-Z]{2}|BFPO ?[0-9]{1,4}|(KY[0-9]|MSR|VG|AI)[ -]?[0-9]{4}|[A-Z]{2} ?[0-9]{2}|GE ?CX|GIR ?0A{2}|SAN ?TA1)$", postcode
20+
r"^(([A-Z]{1,2}[0-9][A-Z0-9]?|ASCN|STHL|TDCU|BBND|[BFS]IQQ|PCRN|TKCA) ?[0-9][A-Z]{2}|BFPO ?[0-9]{1,4}|(KY[0-9]|MSR|VG|AI)[ -]?[0-9]{4}|[A-Z]{2} ?[0-9]{2}|GE ?CX|GIR ?0A{2}|SAN ?TA1)$",
21+
postcode
822
))
923
return result

pyproject.toml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
[tool.pytest.ini_options]
2+
testpaths = ["tests"]
3+
python_files = ["test_*.py"]
4+
python_classes = ["Test*"]
5+
python_functions = ["test_*"]
6+

setup.cfg

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
[bumpversion]
2-
current_version = 1.0.2
2+
current_version = 1.0.3
33
commit = True
44
tag = True
55

@@ -20,6 +20,3 @@ max-line-length = 205
2020

2121
[aliases]
2222
test = pytest
23-
24-
[tool:pytest]
25-
collect_ignore = ['setup.py']

setup.py

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -27,16 +27,18 @@
2727
setup(
2828
author="AMAR DESAI",
2929
author_email='amardesai.bgm@gmail.com',
30-
python_requires='>=3.6',
30+
python_requires='>=3.7',
3131
classifiers=[
32-
'Development Status :: 2 - Pre-Alpha',
32+
'Development Status :: 4 - Beta',
3333
'Intended Audience :: Developers',
3434
'License :: OSI Approved :: MIT License',
3535
'Natural Language :: English',
36-
'Programming Language :: Python :: 3.6',
3736
'Programming Language :: Python :: 3.7',
3837
'Programming Language :: Python :: 3.8',
3938
'Programming Language :: Python :: 3.9',
39+
'Programming Language :: Python :: 3.10',
40+
'Programming Language :: Python :: 3.11',
41+
'Programming Language :: Python :: 3.12',
4042
],
4143
description="package supports validating and formatting postcodes for the UK",
4244
install_requires=install_requires,
@@ -52,6 +54,6 @@
5254
test_suite='tests',
5355
tests_require=test_requirements,
5456
url='https://github.com/myselfdesai/ideal_ukpostcode',
55-
version='1.0.2',
57+
version='1.0.3',
5658
zip_safe=False,
5759
)

0 commit comments

Comments
 (0)