PyPaf 0.6.0__tar.gz → 0.8.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (67) hide show
  1. pypaf-0.8.0/PKG-INFO +184 -0
  2. pypaf-0.8.0/README.md +169 -0
  3. pypaf-0.8.0/src/PyPaf.egg-info/PKG-INFO +184 -0
  4. {pypaf-0.6.0 → pypaf-0.8.0}/src/PyPaf.egg-info/SOURCES.txt +15 -3
  5. {pypaf-0.6.0 → pypaf-0.8.0}/src/paf/address.py +11 -11
  6. pypaf-0.8.0/src/paf/attribute.py +43 -0
  7. pypaf-0.8.0/src/paf/initiator.py +22 -0
  8. pypaf-0.8.0/src/paf/lineable.py +33 -0
  9. pypaf-0.8.0/src/paf/premises/attribute.py +39 -0
  10. pypaf-0.8.0/src/paf/premises/building_type.py +29 -0
  11. pypaf-0.8.0/src/paf/premises/dependent_premisable.py +34 -0
  12. pypaf-0.8.0/src/paf/premises/exception.py +51 -0
  13. pypaf-0.8.0/src/paf/premises/extender.py +25 -0
  14. pypaf-0.8.0/src/paf/premises/lineable.py +26 -0
  15. pypaf-0.8.0/src/paf/premises/premisable.py +149 -0
  16. pypaf-0.8.0/src/paf/premises/premises.py +102 -0
  17. pypaf-0.8.0/src/paf/premises/rule000.py +37 -0
  18. pypaf-0.8.0/src/paf/premises/rule001.py +16 -0
  19. pypaf-0.8.0/src/paf/premises/rule010.py +20 -0
  20. pypaf-0.8.0/src/paf/premises/rule011.py +16 -0
  21. pypaf-0.8.0/src/paf/premises/rule100.py +46 -0
  22. pypaf-0.8.0/src/paf/premises/rule101.py +18 -0
  23. {pypaf-0.6.0 → pypaf-0.8.0}/src/paf/premises/rule110.py +7 -7
  24. pypaf-0.8.0/src/paf/premises/rule111.py +24 -0
  25. pypaf-0.8.0/src/paf/premises/split.py +57 -0
  26. {pypaf-0.6.0 → pypaf-0.8.0}/src/paf/thoroughfare_locality.py +29 -11
  27. pypaf-0.8.0/src/paf/version.py +3 -0
  28. {pypaf-0.6.0 → pypaf-0.8.0}/tests/test_empty.py +5 -0
  29. {pypaf-0.6.0 → pypaf-0.8.0}/tests/test_exception_i.py +5 -0
  30. {pypaf-0.6.0 → pypaf-0.8.0}/tests/test_exception_ii.py +5 -0
  31. {pypaf-0.6.0 → pypaf-0.8.0}/tests/test_exception_iii.py +5 -0
  32. {pypaf-0.6.0 → pypaf-0.8.0}/tests/test_exception_iv.py +83 -0
  33. pypaf-0.8.0/tests/test_kwargs.py +66 -0
  34. {pypaf-0.6.0 → pypaf-0.8.0}/tests/test_mainfile.py +10 -0
  35. {pypaf-0.6.0 → pypaf-0.8.0}/tests/test_po_box.py +5 -0
  36. pypaf-0.8.0/tests/test_premises.py +78 -0
  37. {pypaf-0.6.0 → pypaf-0.8.0}/tests/test_rule_1.py +5 -0
  38. {pypaf-0.6.0 → pypaf-0.8.0}/tests/test_rule_2.py +5 -0
  39. {pypaf-0.6.0 → pypaf-0.8.0}/tests/test_rule_3.py +30 -0
  40. {pypaf-0.6.0 → pypaf-0.8.0}/tests/test_rule_4.py +5 -0
  41. {pypaf-0.6.0 → pypaf-0.8.0}/tests/test_rule_5.py +12 -0
  42. {pypaf-0.6.0 → pypaf-0.8.0}/tests/test_rule_6.py +25 -0
  43. {pypaf-0.6.0 → pypaf-0.8.0}/tests/test_rule_7.py +32 -0
  44. pypaf-0.8.0/tests/test_rule_x.py +121 -0
  45. pypaf-0.6.0/PKG-INFO +0 -117
  46. pypaf-0.6.0/README.md +0 -102
  47. pypaf-0.6.0/src/PyPaf.egg-info/PKG-INFO +0 -117
  48. pypaf-0.6.0/src/paf/attribute.py +0 -66
  49. pypaf-0.6.0/src/paf/lineable.py +0 -39
  50. pypaf-0.6.0/src/paf/premises/common.py +0 -38
  51. pypaf-0.6.0/src/paf/premises/rule000.py +0 -16
  52. pypaf-0.6.0/src/paf/premises/rule001.py +0 -16
  53. pypaf-0.6.0/src/paf/premises/rule010.py +0 -55
  54. pypaf-0.6.0/src/paf/premises/rule011.py +0 -16
  55. pypaf-0.6.0/src/paf/premises/rule101.py +0 -30
  56. pypaf-0.6.0/src/paf/premises/rule111.py +0 -30
  57. pypaf-0.6.0/src/paf/premises_extender.py +0 -27
  58. pypaf-0.6.0/src/paf/version.py +0 -3
  59. {pypaf-0.6.0 → pypaf-0.8.0}/LICENSE.txt +0 -0
  60. {pypaf-0.6.0 → pypaf-0.8.0}/pyproject.toml +0 -0
  61. {pypaf-0.6.0 → pypaf-0.8.0}/setup.cfg +0 -0
  62. {pypaf-0.6.0 → pypaf-0.8.0}/src/PyPaf.egg-info/dependency_links.txt +0 -0
  63. {pypaf-0.6.0 → pypaf-0.8.0}/src/PyPaf.egg-info/top_level.txt +0 -0
  64. {pypaf-0.6.0 → pypaf-0.8.0}/src/paf/__init__.py +0 -0
  65. {pypaf-0.6.0 → pypaf-0.8.0}/src/paf/immutable.py +0 -0
  66. {pypaf-0.6.0 → pypaf-0.8.0}/src/paf/premises/__init__.py +0 -0
  67. {pypaf-0.6.0 → pypaf-0.8.0}/tests/test_immutability.py +0 -0
pypaf-0.8.0/PKG-INFO ADDED
@@ -0,0 +1,184 @@
1
+ Metadata-Version: 2.2
2
+ Name: PyPaf
3
+ Version: 0.8.0
4
+ Summary: Formats the elements of a Royal Mail Postcode Address File entry
5
+ Author-email: John Bard <johnbard@globalnet.co.uk>
6
+ License: MIT
7
+ Project-URL: Homepage, https://github.com/drabjay/pypaf
8
+ Project-URL: Repository, https://github.com/drabjay/pypaf.git
9
+ Project-URL: Issues, https://github.com/drabjay/paf/issues
10
+ Classifier: Programming Language :: Python :: 3
11
+ Classifier: Operating System :: OS Independent
12
+ Requires-Python: >=3.8
13
+ Description-Content-Type: text/markdown
14
+ License-File: LICENSE.txt
15
+
16
+ # PyPaf
17
+
18
+ Formats the elements of a Royal Mail Postcode Address File entry according to the rules described in the [Royal Mail Programmer's Guide Edition 7, Version 6.2](https://www.poweredbypaf.com/wp-content/uploads/2024/11/Latest-Programmers_guide_Edition-7-Version-6-2.pdf)
19
+
20
+ ## Installation
21
+
22
+ Install it from PyPI:
23
+
24
+ pip install pypaf
25
+
26
+ ## Usage
27
+
28
+ ### Formatting
29
+
30
+ May be used to format the PAF Address elements - passed as either a single dictionary or as a series of keyword arguments - as a list of strings:
31
+
32
+ ```python
33
+ import paf
34
+ address = paf.Address({
35
+ 'building_name': "1-2",
36
+ 'thoroughfare_name': "NURSERY",
37
+ 'thoroughfare_descriptor': "LANE",
38
+ 'dependent_locality': "PENN",
39
+ 'post_town': "HIGH WYCOMBE",
40
+ 'postcode': "HP10 8LS"
41
+ })
42
+ address.as_list() # or list(address)
43
+
44
+ ['1-2 NURSERY LANE', 'PENN', 'HIGH WYCOMBE', 'HP10 8LS']
45
+ ```
46
+
47
+ Or as a tuple of strings:
48
+
49
+ ```python
50
+ import paf
51
+ address = paf.Address(
52
+ building_name="1-2",
53
+ thoroughfare_name="NURSERY",
54
+ thoroughfare_descriptor="LANE",
55
+ dependent_locality="PENN",
56
+ post_town="HIGH WYCOMBE",
57
+ postcode="HP10 8LS"
58
+ )
59
+ address.as_tuple() # or tuple(address)
60
+
61
+ ('1-2 NURSERY LANE', 'PENN', 'HIGH WYCOMBE', 'HP10 8LS')
62
+ ```
63
+
64
+ Or as a single string:
65
+
66
+ ```python
67
+ import paf
68
+ address = paf.Address({
69
+ 'building_name': "1-2",
70
+ 'thoroughfare_name': "NURSERY",
71
+ 'thoroughfare_descriptor': "LANE",
72
+ 'dependent_locality': "PENN",
73
+ 'post_town': "HIGH WYCOMBE",
74
+ 'postcode': "HP10 8LS"
75
+ })
76
+ address.as_str() # or str(address)
77
+
78
+ '1-2 NURSERY LANE, PENN, HIGH WYCOMBE. HP10 8LS'
79
+ ```
80
+
81
+ Or as a dictionary:
82
+
83
+ ```python
84
+ import paf
85
+ address = paf.Address(
86
+ building_name="1-2",
87
+ thoroughfare_name="NURSERY",
88
+ thoroughfare_descriptor="LANE",
89
+ dependent_locality="PENN",
90
+ post_town="HIGH WYCOMBE",
91
+ postcode="HP10 8LS"
92
+ )
93
+ address.as_dict()
94
+
95
+ {
96
+ 'line_1': "1-2 NURSERY LANE",
97
+ 'line_2': "PENN",
98
+ 'post_town': "HIGH WYCOMBE",
99
+ 'postcode': "HP10 8LS"
100
+ }
101
+ ```
102
+
103
+ ### Premises Attributes
104
+
105
+ The `sub_building_name`, `building_name` and `building_number` supplied in the source PAF Address elements need to be parsed according Programmer's Guide rules in order to correctly identify the premises elements of the address.
106
+
107
+ The Address class includes a parsed premises dictionary that contains key-values that may be used to identify the premises within the thoroughfare and the sub-premises within the premises.
108
+
109
+ The parsing decomposes the premises and sub-premises to its constituent parts:
110
+
111
+ | Key | Notes |
112
+ | ------------------- | ----------- |
113
+ | premises_type | If it is of a known type e.g. BLOCK, BUILDING |
114
+ | premises_number | Building number or leading digits of building name |
115
+ | premises_suffix | Non-numeric characters following leading digits of building name |
116
+ | premises_name | Building name, if it cannot be decomposed |
117
+ | sub_premises_type | If it is of a known type e.g. FLAT, UNIT |
118
+ | sub_premises_number | Leading digits of sub-building name |
119
+ | sub_premises_suffix | Non-numeric characters following leading digits of sub-building name |
120
+ | sub_premises_name | Sub-building name, if it cannot be decomposed |
121
+
122
+ ```python
123
+ import paf
124
+ self.address = paf.Address({
125
+ 'sub_building_name': "FLAT 2B",
126
+ 'building_name': "THE TOWER",
127
+ 'building_number': "27",
128
+ 'thoroughfare_name': "JOHN",
129
+ 'thoroughfare_descriptor': "STREET",
130
+ 'post_town': "WINCHESTER",
131
+ 'postcode': "SO23 9AP"
132
+ })
133
+ address.premises()
134
+
135
+ {
136
+ 'premises_number': 27,
137
+ 'premises_name': 'THE TOWER',
138
+ 'sub_premises_type': 'FLAT',
139
+ 'sub_premises_number': 2,
140
+ 'sub_premises_suffix': 'B',
141
+ }
142
+ ```
143
+
144
+ If there are no `sub_building` or `building` elements supplied the `organisation_name` or `po_box_number` elements will be used populate the premises elements, where available.
145
+
146
+ If there is no `sub_building_name` element and the `dependent_thoroughfare` elements are populated the `building` elements will be used to populate the `sub_premises` elements and the `dependent_thoroughfare` elements the `premises` elements.
147
+
148
+ ```python
149
+ import paf
150
+ self.address = paf.Address(
151
+ building_name="1A",
152
+ dependent_thoroughfare_name="SEASTONE",
153
+ dependent_thoroughfare_descriptor="COURT",
154
+ thoroughfare_name="STATION",
155
+ thoroughfare_descriptor="ROAD",
156
+ post_town="HOLT",
157
+ postcode="NR25 7HG"
158
+ )
159
+ address.premises()
160
+
161
+ {
162
+ 'premises_name': 'SEASTONE COURT',
163
+ 'sub_premises_number': 1,
164
+ 'sub_premises_suffix': 'A'
165
+ }
166
+ ```
167
+
168
+ ## Development
169
+
170
+ After checking out the repo, run `pytest` to run the tests.
171
+
172
+ To release a new version, update the version number in `version.py`, and then run `python -m build`, which will create a distribution archive. Run `python -m twine upload dist/*`, to upload the distribution archive to [pypi.org](https://pypi.org).
173
+
174
+ ## Contributing
175
+
176
+ Bug reports and pull requests are welcome on GitHub at https://github.com/drabjay/pypaf. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the [Contributor Covenant](http://contributor-covenant.org) code of conduct.
177
+
178
+ ## License
179
+
180
+ The package is available as open source under the terms of the [MIT License](http://opensource.org/licenses/MIT).
181
+
182
+ ## Code of Conduct
183
+
184
+ Everyone interacting in the PyPaf project’s codebases, issue trackers, chat rooms and mailing lists is expected to follow the [code of conduct](https://github.com/drabjayc/pypaf/blob/master/CODE_OF_CONDUCT.md).
pypaf-0.8.0/README.md ADDED
@@ -0,0 +1,169 @@
1
+ # PyPaf
2
+
3
+ Formats the elements of a Royal Mail Postcode Address File entry according to the rules described in the [Royal Mail Programmer's Guide Edition 7, Version 6.2](https://www.poweredbypaf.com/wp-content/uploads/2024/11/Latest-Programmers_guide_Edition-7-Version-6-2.pdf)
4
+
5
+ ## Installation
6
+
7
+ Install it from PyPI:
8
+
9
+ pip install pypaf
10
+
11
+ ## Usage
12
+
13
+ ### Formatting
14
+
15
+ May be used to format the PAF Address elements - passed as either a single dictionary or as a series of keyword arguments - as a list of strings:
16
+
17
+ ```python
18
+ import paf
19
+ address = paf.Address({
20
+ 'building_name': "1-2",
21
+ 'thoroughfare_name': "NURSERY",
22
+ 'thoroughfare_descriptor': "LANE",
23
+ 'dependent_locality': "PENN",
24
+ 'post_town': "HIGH WYCOMBE",
25
+ 'postcode': "HP10 8LS"
26
+ })
27
+ address.as_list() # or list(address)
28
+
29
+ ['1-2 NURSERY LANE', 'PENN', 'HIGH WYCOMBE', 'HP10 8LS']
30
+ ```
31
+
32
+ Or as a tuple of strings:
33
+
34
+ ```python
35
+ import paf
36
+ address = paf.Address(
37
+ building_name="1-2",
38
+ thoroughfare_name="NURSERY",
39
+ thoroughfare_descriptor="LANE",
40
+ dependent_locality="PENN",
41
+ post_town="HIGH WYCOMBE",
42
+ postcode="HP10 8LS"
43
+ )
44
+ address.as_tuple() # or tuple(address)
45
+
46
+ ('1-2 NURSERY LANE', 'PENN', 'HIGH WYCOMBE', 'HP10 8LS')
47
+ ```
48
+
49
+ Or as a single string:
50
+
51
+ ```python
52
+ import paf
53
+ address = paf.Address({
54
+ 'building_name': "1-2",
55
+ 'thoroughfare_name': "NURSERY",
56
+ 'thoroughfare_descriptor': "LANE",
57
+ 'dependent_locality': "PENN",
58
+ 'post_town': "HIGH WYCOMBE",
59
+ 'postcode': "HP10 8LS"
60
+ })
61
+ address.as_str() # or str(address)
62
+
63
+ '1-2 NURSERY LANE, PENN, HIGH WYCOMBE. HP10 8LS'
64
+ ```
65
+
66
+ Or as a dictionary:
67
+
68
+ ```python
69
+ import paf
70
+ address = paf.Address(
71
+ building_name="1-2",
72
+ thoroughfare_name="NURSERY",
73
+ thoroughfare_descriptor="LANE",
74
+ dependent_locality="PENN",
75
+ post_town="HIGH WYCOMBE",
76
+ postcode="HP10 8LS"
77
+ )
78
+ address.as_dict()
79
+
80
+ {
81
+ 'line_1': "1-2 NURSERY LANE",
82
+ 'line_2': "PENN",
83
+ 'post_town': "HIGH WYCOMBE",
84
+ 'postcode': "HP10 8LS"
85
+ }
86
+ ```
87
+
88
+ ### Premises Attributes
89
+
90
+ The `sub_building_name`, `building_name` and `building_number` supplied in the source PAF Address elements need to be parsed according Programmer's Guide rules in order to correctly identify the premises elements of the address.
91
+
92
+ The Address class includes a parsed premises dictionary that contains key-values that may be used to identify the premises within the thoroughfare and the sub-premises within the premises.
93
+
94
+ The parsing decomposes the premises and sub-premises to its constituent parts:
95
+
96
+ | Key | Notes |
97
+ | ------------------- | ----------- |
98
+ | premises_type | If it is of a known type e.g. BLOCK, BUILDING |
99
+ | premises_number | Building number or leading digits of building name |
100
+ | premises_suffix | Non-numeric characters following leading digits of building name |
101
+ | premises_name | Building name, if it cannot be decomposed |
102
+ | sub_premises_type | If it is of a known type e.g. FLAT, UNIT |
103
+ | sub_premises_number | Leading digits of sub-building name |
104
+ | sub_premises_suffix | Non-numeric characters following leading digits of sub-building name |
105
+ | sub_premises_name | Sub-building name, if it cannot be decomposed |
106
+
107
+ ```python
108
+ import paf
109
+ self.address = paf.Address({
110
+ 'sub_building_name': "FLAT 2B",
111
+ 'building_name': "THE TOWER",
112
+ 'building_number': "27",
113
+ 'thoroughfare_name': "JOHN",
114
+ 'thoroughfare_descriptor': "STREET",
115
+ 'post_town': "WINCHESTER",
116
+ 'postcode': "SO23 9AP"
117
+ })
118
+ address.premises()
119
+
120
+ {
121
+ 'premises_number': 27,
122
+ 'premises_name': 'THE TOWER',
123
+ 'sub_premises_type': 'FLAT',
124
+ 'sub_premises_number': 2,
125
+ 'sub_premises_suffix': 'B',
126
+ }
127
+ ```
128
+
129
+ If there are no `sub_building` or `building` elements supplied the `organisation_name` or `po_box_number` elements will be used populate the premises elements, where available.
130
+
131
+ If there is no `sub_building_name` element and the `dependent_thoroughfare` elements are populated the `building` elements will be used to populate the `sub_premises` elements and the `dependent_thoroughfare` elements the `premises` elements.
132
+
133
+ ```python
134
+ import paf
135
+ self.address = paf.Address(
136
+ building_name="1A",
137
+ dependent_thoroughfare_name="SEASTONE",
138
+ dependent_thoroughfare_descriptor="COURT",
139
+ thoroughfare_name="STATION",
140
+ thoroughfare_descriptor="ROAD",
141
+ post_town="HOLT",
142
+ postcode="NR25 7HG"
143
+ )
144
+ address.premises()
145
+
146
+ {
147
+ 'premises_name': 'SEASTONE COURT',
148
+ 'sub_premises_number': 1,
149
+ 'sub_premises_suffix': 'A'
150
+ }
151
+ ```
152
+
153
+ ## Development
154
+
155
+ After checking out the repo, run `pytest` to run the tests.
156
+
157
+ To release a new version, update the version number in `version.py`, and then run `python -m build`, which will create a distribution archive. Run `python -m twine upload dist/*`, to upload the distribution archive to [pypi.org](https://pypi.org).
158
+
159
+ ## Contributing
160
+
161
+ Bug reports and pull requests are welcome on GitHub at https://github.com/drabjay/pypaf. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the [Contributor Covenant](http://contributor-covenant.org) code of conduct.
162
+
163
+ ## License
164
+
165
+ The package is available as open source under the terms of the [MIT License](http://opensource.org/licenses/MIT).
166
+
167
+ ## Code of Conduct
168
+
169
+ Everyone interacting in the PyPaf project’s codebases, issue trackers, chat rooms and mailing lists is expected to follow the [code of conduct](https://github.com/drabjayc/pypaf/blob/master/CODE_OF_CONDUCT.md).
@@ -0,0 +1,184 @@
1
+ Metadata-Version: 2.2
2
+ Name: PyPaf
3
+ Version: 0.8.0
4
+ Summary: Formats the elements of a Royal Mail Postcode Address File entry
5
+ Author-email: John Bard <johnbard@globalnet.co.uk>
6
+ License: MIT
7
+ Project-URL: Homepage, https://github.com/drabjay/pypaf
8
+ Project-URL: Repository, https://github.com/drabjay/pypaf.git
9
+ Project-URL: Issues, https://github.com/drabjay/paf/issues
10
+ Classifier: Programming Language :: Python :: 3
11
+ Classifier: Operating System :: OS Independent
12
+ Requires-Python: >=3.8
13
+ Description-Content-Type: text/markdown
14
+ License-File: LICENSE.txt
15
+
16
+ # PyPaf
17
+
18
+ Formats the elements of a Royal Mail Postcode Address File entry according to the rules described in the [Royal Mail Programmer's Guide Edition 7, Version 6.2](https://www.poweredbypaf.com/wp-content/uploads/2024/11/Latest-Programmers_guide_Edition-7-Version-6-2.pdf)
19
+
20
+ ## Installation
21
+
22
+ Install it from PyPI:
23
+
24
+ pip install pypaf
25
+
26
+ ## Usage
27
+
28
+ ### Formatting
29
+
30
+ May be used to format the PAF Address elements - passed as either a single dictionary or as a series of keyword arguments - as a list of strings:
31
+
32
+ ```python
33
+ import paf
34
+ address = paf.Address({
35
+ 'building_name': "1-2",
36
+ 'thoroughfare_name': "NURSERY",
37
+ 'thoroughfare_descriptor': "LANE",
38
+ 'dependent_locality': "PENN",
39
+ 'post_town': "HIGH WYCOMBE",
40
+ 'postcode': "HP10 8LS"
41
+ })
42
+ address.as_list() # or list(address)
43
+
44
+ ['1-2 NURSERY LANE', 'PENN', 'HIGH WYCOMBE', 'HP10 8LS']
45
+ ```
46
+
47
+ Or as a tuple of strings:
48
+
49
+ ```python
50
+ import paf
51
+ address = paf.Address(
52
+ building_name="1-2",
53
+ thoroughfare_name="NURSERY",
54
+ thoroughfare_descriptor="LANE",
55
+ dependent_locality="PENN",
56
+ post_town="HIGH WYCOMBE",
57
+ postcode="HP10 8LS"
58
+ )
59
+ address.as_tuple() # or tuple(address)
60
+
61
+ ('1-2 NURSERY LANE', 'PENN', 'HIGH WYCOMBE', 'HP10 8LS')
62
+ ```
63
+
64
+ Or as a single string:
65
+
66
+ ```python
67
+ import paf
68
+ address = paf.Address({
69
+ 'building_name': "1-2",
70
+ 'thoroughfare_name': "NURSERY",
71
+ 'thoroughfare_descriptor': "LANE",
72
+ 'dependent_locality': "PENN",
73
+ 'post_town': "HIGH WYCOMBE",
74
+ 'postcode': "HP10 8LS"
75
+ })
76
+ address.as_str() # or str(address)
77
+
78
+ '1-2 NURSERY LANE, PENN, HIGH WYCOMBE. HP10 8LS'
79
+ ```
80
+
81
+ Or as a dictionary:
82
+
83
+ ```python
84
+ import paf
85
+ address = paf.Address(
86
+ building_name="1-2",
87
+ thoroughfare_name="NURSERY",
88
+ thoroughfare_descriptor="LANE",
89
+ dependent_locality="PENN",
90
+ post_town="HIGH WYCOMBE",
91
+ postcode="HP10 8LS"
92
+ )
93
+ address.as_dict()
94
+
95
+ {
96
+ 'line_1': "1-2 NURSERY LANE",
97
+ 'line_2': "PENN",
98
+ 'post_town': "HIGH WYCOMBE",
99
+ 'postcode': "HP10 8LS"
100
+ }
101
+ ```
102
+
103
+ ### Premises Attributes
104
+
105
+ The `sub_building_name`, `building_name` and `building_number` supplied in the source PAF Address elements need to be parsed according Programmer's Guide rules in order to correctly identify the premises elements of the address.
106
+
107
+ The Address class includes a parsed premises dictionary that contains key-values that may be used to identify the premises within the thoroughfare and the sub-premises within the premises.
108
+
109
+ The parsing decomposes the premises and sub-premises to its constituent parts:
110
+
111
+ | Key | Notes |
112
+ | ------------------- | ----------- |
113
+ | premises_type | If it is of a known type e.g. BLOCK, BUILDING |
114
+ | premises_number | Building number or leading digits of building name |
115
+ | premises_suffix | Non-numeric characters following leading digits of building name |
116
+ | premises_name | Building name, if it cannot be decomposed |
117
+ | sub_premises_type | If it is of a known type e.g. FLAT, UNIT |
118
+ | sub_premises_number | Leading digits of sub-building name |
119
+ | sub_premises_suffix | Non-numeric characters following leading digits of sub-building name |
120
+ | sub_premises_name | Sub-building name, if it cannot be decomposed |
121
+
122
+ ```python
123
+ import paf
124
+ self.address = paf.Address({
125
+ 'sub_building_name': "FLAT 2B",
126
+ 'building_name': "THE TOWER",
127
+ 'building_number': "27",
128
+ 'thoroughfare_name': "JOHN",
129
+ 'thoroughfare_descriptor': "STREET",
130
+ 'post_town': "WINCHESTER",
131
+ 'postcode': "SO23 9AP"
132
+ })
133
+ address.premises()
134
+
135
+ {
136
+ 'premises_number': 27,
137
+ 'premises_name': 'THE TOWER',
138
+ 'sub_premises_type': 'FLAT',
139
+ 'sub_premises_number': 2,
140
+ 'sub_premises_suffix': 'B',
141
+ }
142
+ ```
143
+
144
+ If there are no `sub_building` or `building` elements supplied the `organisation_name` or `po_box_number` elements will be used populate the premises elements, where available.
145
+
146
+ If there is no `sub_building_name` element and the `dependent_thoroughfare` elements are populated the `building` elements will be used to populate the `sub_premises` elements and the `dependent_thoroughfare` elements the `premises` elements.
147
+
148
+ ```python
149
+ import paf
150
+ self.address = paf.Address(
151
+ building_name="1A",
152
+ dependent_thoroughfare_name="SEASTONE",
153
+ dependent_thoroughfare_descriptor="COURT",
154
+ thoroughfare_name="STATION",
155
+ thoroughfare_descriptor="ROAD",
156
+ post_town="HOLT",
157
+ postcode="NR25 7HG"
158
+ )
159
+ address.premises()
160
+
161
+ {
162
+ 'premises_name': 'SEASTONE COURT',
163
+ 'sub_premises_number': 1,
164
+ 'sub_premises_suffix': 'A'
165
+ }
166
+ ```
167
+
168
+ ## Development
169
+
170
+ After checking out the repo, run `pytest` to run the tests.
171
+
172
+ To release a new version, update the version number in `version.py`, and then run `python -m build`, which will create a distribution archive. Run `python -m twine upload dist/*`, to upload the distribution archive to [pypi.org](https://pypi.org).
173
+
174
+ ## Contributing
175
+
176
+ Bug reports and pull requests are welcome on GitHub at https://github.com/drabjay/pypaf. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the [Contributor Covenant](http://contributor-covenant.org) code of conduct.
177
+
178
+ ## License
179
+
180
+ The package is available as open source under the terms of the [MIT License](http://opensource.org/licenses/MIT).
181
+
182
+ ## Code of Conduct
183
+
184
+ Everyone interacting in the PyPaf project’s codebases, issue trackers, chat rooms and mailing lists is expected to follow the [code of conduct](https://github.com/drabjayc/pypaf/blob/master/CODE_OF_CONDUCT.md).
@@ -9,31 +9,43 @@ src/paf/__init__.py
9
9
  src/paf/address.py
10
10
  src/paf/attribute.py
11
11
  src/paf/immutable.py
12
+ src/paf/initiator.py
12
13
  src/paf/lineable.py
13
- src/paf/premises_extender.py
14
14
  src/paf/thoroughfare_locality.py
15
15
  src/paf/version.py
16
16
  src/paf/premises/__init__.py
17
- src/paf/premises/common.py
17
+ src/paf/premises/attribute.py
18
+ src/paf/premises/building_type.py
19
+ src/paf/premises/dependent_premisable.py
20
+ src/paf/premises/exception.py
21
+ src/paf/premises/extender.py
22
+ src/paf/premises/lineable.py
23
+ src/paf/premises/premisable.py
24
+ src/paf/premises/premises.py
18
25
  src/paf/premises/rule000.py
19
26
  src/paf/premises/rule001.py
20
27
  src/paf/premises/rule010.py
21
28
  src/paf/premises/rule011.py
29
+ src/paf/premises/rule100.py
22
30
  src/paf/premises/rule101.py
23
31
  src/paf/premises/rule110.py
24
32
  src/paf/premises/rule111.py
33
+ src/paf/premises/split.py
25
34
  tests/test_empty.py
26
35
  tests/test_exception_i.py
27
36
  tests/test_exception_ii.py
28
37
  tests/test_exception_iii.py
29
38
  tests/test_exception_iv.py
30
39
  tests/test_immutability.py
40
+ tests/test_kwargs.py
31
41
  tests/test_mainfile.py
32
42
  tests/test_po_box.py
43
+ tests/test_premises.py
33
44
  tests/test_rule_1.py
34
45
  tests/test_rule_2.py
35
46
  tests/test_rule_3.py
36
47
  tests/test_rule_4.py
37
48
  tests/test_rule_5.py
38
49
  tests/test_rule_6.py
39
- tests/test_rule_7.py
50
+ tests/test_rule_7.py
51
+ tests/test_rule_x.py
@@ -1,24 +1,24 @@
1
1
  """PAF Address"""
2
2
 
3
3
  # Tried using dataclasses.dataclass(frozen=True) decorator for immutablity but did not work"""
4
+ from .initiator import attribute_init
5
+ from .attribute import AttributeMixin
4
6
  from .immutable import ImmutableMixin
5
7
  from .lineable import LineableMixin
8
+ from .thoroughfare_locality import ThoroughfareLocalityMixin
9
+ from .premises.premises import Premises
6
10
 
7
- class Address(ImmutableMixin, LineableMixin):
11
+ class Address(ImmutableMixin, AttributeMixin, ThoroughfareLocalityMixin, LineableMixin):
8
12
  """Main PAF Address class"""
9
13
 
10
- def __init__(self, args):
14
+ @attribute_init
15
+ def __init__(self, *args, **kwargs):
11
16
  """Initialise Address elements"""
12
- for key in self.__class__.attrs: # pylint: disable=not-an-iterable
13
- object.__setattr__(self, key, '')
14
- for key, val in args.items():
15
- if hasattr(self, key):
16
- object.__setattr__(self, key, val)
17
- self.extend_premises()
17
+ object.__setattr__(self, 'premises', Premises(*args, **kwargs))
18
18
 
19
19
  def __repr__(self):
20
20
  """Return full representation of an Address"""
21
- args = {k: getattr(self, k) for k in self.__class__.attrs if getattr(self, k, None)} # pylint: disable=not-an-iterable
21
+ args = {k: getattr(self, k) for k in list(self.attrs) if getattr(self, k, None)}
22
22
  return self.__class__.__name__ + '(' + str(args) + ')'
23
23
 
24
24
  def __str__(self):
@@ -30,9 +30,9 @@ class Address(ImmutableMixin, LineableMixin):
30
30
 
31
31
  def __iter__(self):
32
32
  """Return Address as iterable"""
33
- yield from self.lines.__iter__()
33
+ yield from list(self.lines)
34
34
  if not self.is_empty('postcode'):
35
- yield from [getattr(self, 'postcode')].__iter__()
35
+ yield from list([getattr(self, 'postcode')])
36
36
 
37
37
  def as_str(self):
38
38
  """Return Address as string"""
@@ -0,0 +1,43 @@
1
+ """Attribute Mixin"""
2
+
3
+ class AttributeMixin():
4
+ """Address elements and derived properties"""
5
+
6
+ @classmethod
7
+ @property
8
+ def premises_attrs(cls):
9
+ """Returns Paf premises elements"""
10
+ return (
11
+ 'organisation_name', 'department_name',
12
+ 'sub_building_name', 'building_name', 'building_number',
13
+ 'po_box_number'
14
+ )
15
+
16
+ @classmethod
17
+ @property
18
+ def post_attrs(cls):
19
+ """Returns Paf post elements"""
20
+ return ('post_town', 'postcode')
21
+
22
+ @classmethod
23
+ @property
24
+ def other_attrs(cls):
25
+ """Returns Paf other elements"""
26
+ return ('concatenation_indicator', 'udprn')
27
+
28
+ @classmethod
29
+ @property
30
+ def attrs(cls):
31
+ """Returns all Paf address elements"""
32
+ return(
33
+ cls.premises_attrs
34
+ + cls.dependent_thoroughfare_attrs
35
+ + cls.thoroughfare_attrs
36
+ + cls.locality_attrs
37
+ + cls.post_attrs
38
+ + cls.other_attrs
39
+ )
40
+
41
+ def is_empty(self, attr):
42
+ """Returns if attribute value is empty"""
43
+ return getattr(self, attr, '') == ''