openepd 6.13.2__tar.gz → 6.15.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.
- {openepd-6.13.2 → openepd-6.15.0}/PKG-INFO +1 -1
- {openepd-6.13.2 → openepd-6.15.0}/pyproject.toml +33 -8
- {openepd-6.13.2 → openepd-6.15.0}/src/openepd/__version__.py +1 -1
- {openepd-6.13.2 → openepd-6.15.0}/src/openepd/api/average_dataset/generic_estimate_sync_api.py +2 -1
- {openepd-6.13.2 → openepd-6.15.0}/src/openepd/api/average_dataset/industry_epd_sync_api.py +2 -1
- {openepd-6.13.2 → openepd-6.15.0}/src/openepd/api/base_sync_client.py +10 -8
- {openepd-6.13.2 → openepd-6.15.0}/src/openepd/api/common.py +17 -11
- {openepd-6.13.2 → openepd-6.15.0}/src/openepd/api/dto/common.py +1 -1
- {openepd-6.13.2 → openepd-6.15.0}/src/openepd/api/epd/sync_api.py +2 -1
- {openepd-6.13.2 → openepd-6.15.0}/src/openepd/bundle/base.py +5 -4
- {openepd-6.13.2 → openepd-6.15.0}/src/openepd/bundle/reader.py +13 -7
- {openepd-6.13.2 → openepd-6.15.0}/src/openepd/bundle/writer.py +11 -6
- {openepd-6.13.2 → openepd-6.15.0}/src/openepd/compat/pydantic.py +1 -1
- {openepd-6.13.2 → openepd-6.15.0}/src/openepd/m49/__init__.py +2 -0
- {openepd-6.13.2 → openepd-6.15.0}/src/openepd/m49/const.py +1 -1
- {openepd-6.13.2 → openepd-6.15.0}/src/openepd/m49/utils.py +16 -10
- {openepd-6.13.2 → openepd-6.15.0}/src/openepd/model/base.py +19 -15
- {openepd-6.13.2 → openepd-6.15.0}/src/openepd/model/common.py +22 -19
- {openepd-6.13.2 → openepd-6.15.0}/src/openepd/model/declaration.py +2 -2
- {openepd-6.13.2 → openepd-6.15.0}/src/openepd/model/epd.py +2 -1
- {openepd-6.13.2 → openepd-6.15.0}/src/openepd/model/factory.py +5 -3
- {openepd-6.13.2 → openepd-6.15.0}/src/openepd/model/lcia.py +30 -6
- {openepd-6.13.2 → openepd-6.15.0}/src/openepd/model/org.py +6 -3
- {openepd-6.13.2 → openepd-6.15.0}/src/openepd/model/pcr.py +2 -2
- {openepd-6.13.2 → openepd-6.15.0}/src/openepd/model/specs/__init__.py +36 -0
- {openepd-6.13.2 → openepd-6.15.0}/src/openepd/model/specs/asphalt.py +3 -3
- {openepd-6.13.2 → openepd-6.15.0}/src/openepd/model/specs/base.py +2 -1
- {openepd-6.13.2 → openepd-6.15.0}/src/openepd/model/specs/enums.py +695 -695
- {openepd-6.13.2 → openepd-6.15.0}/src/openepd/model/specs/range/cmu.py +0 -3
- {openepd-6.13.2 → openepd-6.15.0}/src/openepd/model/specs/range/furnishings.py +17 -17
- {openepd-6.13.2 → openepd-6.15.0}/src/openepd/model/specs/range/steel.py +16 -11
- {openepd-6.13.2 → openepd-6.15.0}/src/openepd/model/specs/range/wood.py +4 -7
- {openepd-6.13.2 → openepd-6.15.0}/src/openepd/model/specs/singular/__init__.py +109 -1
- {openepd-6.13.2 → openepd-6.15.0}/src/openepd/model/specs/singular/aluminium.py +2 -1
- {openepd-6.13.2 → openepd-6.15.0}/src/openepd/model/specs/singular/furnishings.py +10 -10
- {openepd-6.13.2 → openepd-6.15.0}/src/openepd/model/specs/singular/steel.py +10 -2
- {openepd-6.13.2 → openepd-6.15.0}/src/openepd/model/validation/common.py +10 -6
- {openepd-6.13.2 → openepd-6.15.0}/src/openepd/model/validation/enum.py +4 -2
- {openepd-6.13.2 → openepd-6.15.0}/src/openepd/model/validation/quantity.py +8 -1
- {openepd-6.13.2 → openepd-6.15.0}/src/openepd/model/versioning.py +8 -6
- {openepd-6.13.2 → openepd-6.15.0}/src/openepd/patch_pydantic.py +2 -1
- {openepd-6.13.2 → openepd-6.15.0}/LICENSE +0 -0
- {openepd-6.13.2 → openepd-6.15.0}/README.md +0 -0
- {openepd-6.13.2 → openepd-6.15.0}/src/openepd/__init__.py +0 -0
- {openepd-6.13.2 → openepd-6.15.0}/src/openepd/api/__init__.py +0 -0
- {openepd-6.13.2 → openepd-6.15.0}/src/openepd/api/average_dataset/__init__.py +0 -0
- {openepd-6.13.2 → openepd-6.15.0}/src/openepd/api/category/__init__.py +0 -0
- {openepd-6.13.2 → openepd-6.15.0}/src/openepd/api/category/dto.py +0 -0
- {openepd-6.13.2 → openepd-6.15.0}/src/openepd/api/category/sync_api.py +0 -0
- {openepd-6.13.2 → openepd-6.15.0}/src/openepd/api/dto/__init__.py +0 -0
- {openepd-6.13.2 → openepd-6.15.0}/src/openepd/api/dto/base.py +0 -0
- {openepd-6.13.2 → openepd-6.15.0}/src/openepd/api/dto/meta.py +0 -0
- {openepd-6.13.2 → openepd-6.15.0}/src/openepd/api/dto/mf.py +0 -0
- {openepd-6.13.2 → openepd-6.15.0}/src/openepd/api/dto/params.py +0 -0
- {openepd-6.13.2 → openepd-6.15.0}/src/openepd/api/epd/__init__.py +0 -0
- {openepd-6.13.2 → openepd-6.15.0}/src/openepd/api/epd/dto.py +0 -0
- {openepd-6.13.2 → openepd-6.15.0}/src/openepd/api/errors.py +0 -0
- {openepd-6.13.2 → openepd-6.15.0}/src/openepd/api/pcr/__init__.py +0 -0
- {openepd-6.13.2 → openepd-6.15.0}/src/openepd/api/pcr/sync_api.py +0 -0
- {openepd-6.13.2 → openepd-6.15.0}/src/openepd/api/sync_client.py +0 -0
- {openepd-6.13.2 → openepd-6.15.0}/src/openepd/api/test/__init__.py +0 -0
- {openepd-6.13.2 → openepd-6.15.0}/src/openepd/api/utils.py +0 -0
- {openepd-6.13.2 → openepd-6.15.0}/src/openepd/bundle/__init__.py +0 -0
- {openepd-6.13.2 → openepd-6.15.0}/src/openepd/bundle/model.py +0 -0
- {openepd-6.13.2 → openepd-6.15.0}/src/openepd/compat/__init__.py +0 -0
- {openepd-6.13.2 → openepd-6.15.0}/src/openepd/compat/compat_functional_validators.py +0 -0
- {openepd-6.13.2 → openepd-6.15.0}/src/openepd/model/__init__.py +0 -0
- {openepd-6.13.2 → openepd-6.15.0}/src/openepd/model/category.py +0 -0
- {openepd-6.13.2 → openepd-6.15.0}/src/openepd/model/generic_estimate.py +0 -0
- {openepd-6.13.2 → openepd-6.15.0}/src/openepd/model/geography.py +0 -0
- {openepd-6.13.2 → openepd-6.15.0}/src/openepd/model/industry_epd.py +0 -0
- {openepd-6.13.2 → openepd-6.15.0}/src/openepd/model/specs/README.md +0 -0
- {openepd-6.13.2 → openepd-6.15.0}/src/openepd/model/specs/concrete.py +0 -0
- {openepd-6.13.2 → openepd-6.15.0}/src/openepd/model/specs/range/__init__.py +0 -0
- {openepd-6.13.2 → openepd-6.15.0}/src/openepd/model/specs/range/accessories.py +1 -1
- {openepd-6.13.2 → openepd-6.15.0}/src/openepd/model/specs/range/aggregates.py +0 -0
- {openepd-6.13.2 → openepd-6.15.0}/src/openepd/model/specs/range/aluminium.py +1 -1
- {openepd-6.13.2 → openepd-6.15.0}/src/openepd/model/specs/range/asphalt.py +0 -0
- {openepd-6.13.2 → openepd-6.15.0}/src/openepd/model/specs/range/bulk_materials.py +0 -0
- {openepd-6.13.2 → openepd-6.15.0}/src/openepd/model/specs/range/cast_decks_and_underlayment.py +0 -0
- {openepd-6.13.2 → openepd-6.15.0}/src/openepd/model/specs/range/cladding.py +10 -10
- {openepd-6.13.2 → openepd-6.15.0}/src/openepd/model/specs/range/concrete.py +1 -1
- {openepd-6.13.2 → openepd-6.15.0}/src/openepd/model/specs/range/conveying_equipment.py +2 -2
- {openepd-6.13.2 → openepd-6.15.0}/src/openepd/model/specs/range/electrical.py +18 -18
- {openepd-6.13.2 → openepd-6.15.0}/src/openepd/model/specs/range/electrical_transmission_and_distribution_equipment.py +1 -1
- {openepd-6.13.2 → openepd-6.15.0}/src/openepd/model/specs/range/electricity.py +0 -0
- {openepd-6.13.2 → openepd-6.15.0}/src/openepd/model/specs/range/finishes.py +16 -16
- {openepd-6.13.2 → openepd-6.15.0}/src/openepd/model/specs/range/fire_and_smoke_protection.py +3 -3
- {openepd-6.13.2 → openepd-6.15.0}/src/openepd/model/specs/range/grouting.py +0 -0
- {openepd-6.13.2 → openepd-6.15.0}/src/openepd/model/specs/range/manufacturing_inputs.py +4 -4
- {openepd-6.13.2 → openepd-6.15.0}/src/openepd/model/specs/range/masonry.py +1 -1
- {openepd-6.13.2 → openepd-6.15.0}/src/openepd/model/specs/range/material_handling.py +0 -0
- {openepd-6.13.2 → openepd-6.15.0}/src/openepd/model/specs/range/mechanical.py +6 -6
- {openepd-6.13.2 → openepd-6.15.0}/src/openepd/model/specs/range/mechanical_insulation.py +0 -0
- {openepd-6.13.2 → openepd-6.15.0}/src/openepd/model/specs/range/network_infrastructure.py +3 -3
- {openepd-6.13.2 → openepd-6.15.0}/src/openepd/model/specs/range/openings.py +17 -17
- {openepd-6.13.2 → openepd-6.15.0}/src/openepd/model/specs/range/other_electrical_equipment.py +0 -0
- {openepd-6.13.2 → openepd-6.15.0}/src/openepd/model/specs/range/other_materials.py +4 -4
- {openepd-6.13.2 → openepd-6.15.0}/src/openepd/model/specs/range/plumbing.py +5 -5
- {openepd-6.13.2 → openepd-6.15.0}/src/openepd/model/specs/range/precast_concrete.py +2 -2
- {openepd-6.13.2 → openepd-6.15.0}/src/openepd/model/specs/range/sheathing.py +0 -0
- {openepd-6.13.2 → openepd-6.15.0}/src/openepd/model/specs/range/thermal_moisture_protection.py +12 -12
- {openepd-6.13.2 → openepd-6.15.0}/src/openepd/model/specs/range/utility_piping.py +0 -0
- {openepd-6.13.2 → openepd-6.15.0}/src/openepd/model/specs/range/wood_joists.py +0 -0
- {openepd-6.13.2 → openepd-6.15.0}/src/openepd/model/specs/singular/accessories.py +0 -0
- {openepd-6.13.2 → openepd-6.15.0}/src/openepd/model/specs/singular/aggregates.py +0 -0
- {openepd-6.13.2 → openepd-6.15.0}/src/openepd/model/specs/singular/asphalt.py +0 -0
- {openepd-6.13.2 → openepd-6.15.0}/src/openepd/model/specs/singular/bulk_materials.py +0 -0
- {openepd-6.13.2 → openepd-6.15.0}/src/openepd/model/specs/singular/cast_decks_and_underlayment.py +0 -0
- {openepd-6.13.2 → openepd-6.15.0}/src/openepd/model/specs/singular/cladding.py +0 -0
- {openepd-6.13.2 → openepd-6.15.0}/src/openepd/model/specs/singular/cmu.py +0 -0
- {openepd-6.13.2 → openepd-6.15.0}/src/openepd/model/specs/singular/common.py +0 -0
- {openepd-6.13.2 → openepd-6.15.0}/src/openepd/model/specs/singular/concrete.py +0 -0
- {openepd-6.13.2 → openepd-6.15.0}/src/openepd/model/specs/singular/conveying_equipment.py +0 -0
- {openepd-6.13.2 → openepd-6.15.0}/src/openepd/model/specs/singular/deprecated/__init__.py +0 -0
- {openepd-6.13.2 → openepd-6.15.0}/src/openepd/model/specs/singular/deprecated/concrete.py +0 -0
- {openepd-6.13.2 → openepd-6.15.0}/src/openepd/model/specs/singular/deprecated/steel.py +0 -0
- {openepd-6.13.2 → openepd-6.15.0}/src/openepd/model/specs/singular/electrical.py +0 -0
- {openepd-6.13.2 → openepd-6.15.0}/src/openepd/model/specs/singular/electrical_transmission_and_distribution_equipment.py +0 -0
- {openepd-6.13.2 → openepd-6.15.0}/src/openepd/model/specs/singular/electricity.py +0 -0
- {openepd-6.13.2 → openepd-6.15.0}/src/openepd/model/specs/singular/finishes.py +0 -0
- {openepd-6.13.2 → openepd-6.15.0}/src/openepd/model/specs/singular/fire_and_smoke_protection.py +0 -0
- {openepd-6.13.2 → openepd-6.15.0}/src/openepd/model/specs/singular/grouting.py +0 -0
- {openepd-6.13.2 → openepd-6.15.0}/src/openepd/model/specs/singular/manufacturing_inputs.py +0 -0
- {openepd-6.13.2 → openepd-6.15.0}/src/openepd/model/specs/singular/masonry.py +0 -0
- {openepd-6.13.2 → openepd-6.15.0}/src/openepd/model/specs/singular/material_handling.py +0 -0
- {openepd-6.13.2 → openepd-6.15.0}/src/openepd/model/specs/singular/mechanical.py +0 -0
- {openepd-6.13.2 → openepd-6.15.0}/src/openepd/model/specs/singular/mechanical_insulation.py +0 -0
- {openepd-6.13.2 → openepd-6.15.0}/src/openepd/model/specs/singular/mixins/__init__.py +0 -0
- {openepd-6.13.2 → openepd-6.15.0}/src/openepd/model/specs/singular/mixins/conduit_mixin.py +0 -0
- {openepd-6.13.2 → openepd-6.15.0}/src/openepd/model/specs/singular/network_infrastructure.py +0 -0
- {openepd-6.13.2 → openepd-6.15.0}/src/openepd/model/specs/singular/openings.py +0 -0
- {openepd-6.13.2 → openepd-6.15.0}/src/openepd/model/specs/singular/other_electrical_equipment.py +0 -0
- {openepd-6.13.2 → openepd-6.15.0}/src/openepd/model/specs/singular/other_materials.py +0 -0
- {openepd-6.13.2 → openepd-6.15.0}/src/openepd/model/specs/singular/plumbing.py +0 -0
- {openepd-6.13.2 → openepd-6.15.0}/src/openepd/model/specs/singular/precast_concrete.py +0 -0
- {openepd-6.13.2 → openepd-6.15.0}/src/openepd/model/specs/singular/sheathing.py +0 -0
- {openepd-6.13.2 → openepd-6.15.0}/src/openepd/model/specs/singular/thermal_moisture_protection.py +0 -0
- {openepd-6.13.2 → openepd-6.15.0}/src/openepd/model/specs/singular/utility_piping.py +0 -0
- {openepd-6.13.2 → openepd-6.15.0}/src/openepd/model/specs/singular/wood.py +0 -0
- {openepd-6.13.2 → openepd-6.15.0}/src/openepd/model/specs/singular/wood_joists.py +0 -0
- {openepd-6.13.2 → openepd-6.15.0}/src/openepd/model/standard.py +0 -0
- {openepd-6.13.2 → openepd-6.15.0}/src/openepd/model/validation/__init__.py +0 -0
- {openepd-6.13.2 → openepd-6.15.0}/src/openepd/model/validation/numbers.py +0 -0
- {openepd-6.13.2 → openepd-6.15.0}/src/openepd/py.typed +0 -0
@@ -1,6 +1,6 @@
|
|
1
1
|
[tool.poetry]
|
2
2
|
name = "openepd"
|
3
|
-
version = "6.
|
3
|
+
version = "6.15.0"
|
4
4
|
license = "Apache-2.0"
|
5
5
|
description = "Python library to work with OpenEPD format"
|
6
6
|
authors = ["C-Change Labs <support@c-change-labs.com>"]
|
@@ -31,7 +31,7 @@ openlocationcode = ">=1.0.1"
|
|
31
31
|
# Optional dependencies
|
32
32
|
# lxml = { version = "~=4.9.2", optional = true }
|
33
33
|
|
34
|
-
[tool.poetry.dev
|
34
|
+
[tool.poetry.group.dev.dependencies]
|
35
35
|
# Unit tests
|
36
36
|
coverage = { version = "=6.5", extras = ["toml"] }
|
37
37
|
pytest = "~=7.2"
|
@@ -42,12 +42,8 @@ wheel = "~=0.40.0"
|
|
42
42
|
click = "~=8.1.7"
|
43
43
|
|
44
44
|
# Dev tools
|
45
|
-
|
45
|
+
ruff = ">=0.11.8"
|
46
46
|
licenseheaders = "~=0.8"
|
47
|
-
flake8 = "~=4.0"
|
48
|
-
flake8-import-graph = "~=0.1.3"
|
49
|
-
flake8-docstrings = "~=1.7.0"
|
50
|
-
isort = "~=5.11"
|
51
47
|
mypy = ">=1.0.1"
|
52
48
|
pre-commit = "~=2.19"
|
53
49
|
commitizen = "~=3.16.0"
|
@@ -66,7 +62,6 @@ jinja2 = ">=3.1.4"
|
|
66
62
|
[tool.poetry.extras]
|
67
63
|
api_client = ["requests"]
|
68
64
|
|
69
|
-
|
70
65
|
[tool.commitizen]
|
71
66
|
version_provider = "poetry"
|
72
67
|
bump_version = "bump: version $current_version → $new_version"
|
@@ -146,3 +141,33 @@ follow_imports = "skip"
|
|
146
141
|
[[tool.mypy.overrides]]
|
147
142
|
module = "openlocationcode.*"
|
148
143
|
ignore_missing_imports = true
|
144
|
+
|
145
|
+
[tool.ruff]
|
146
|
+
line-length = 120
|
147
|
+
target-version = "py311"
|
148
|
+
exclude = [".*pyi", "tools/**.py"]
|
149
|
+
|
150
|
+
[tool.ruff.lint.isort]
|
151
|
+
force-sort-within-sections = true
|
152
|
+
|
153
|
+
[tool.ruff.lint]
|
154
|
+
extend-ignore = [
|
155
|
+
"S101", # Use of assert statement. We have a lot of asserts for mypy type checking.
|
156
|
+
"W291", # W291 trailing whitespace
|
157
|
+
"W391", # W391 blank line at end of file
|
158
|
+
"E501", # E501: line too long
|
159
|
+
"E203", # E704: Multiple statements on one line (def)
|
160
|
+
"F403", #F403 'from module import *' used; unable to detect undefined names (F403)
|
161
|
+
##### DOCSTRINGS #####
|
162
|
+
"D100", # Missing docstring in public module
|
163
|
+
"D101", # Missing docstring in public class
|
164
|
+
"D102", # Docstring of prublic method
|
165
|
+
"D107", # Missing docstring in __init__
|
166
|
+
"D105", # Missing docstring in magic method
|
167
|
+
"D104", # Missing docstring in public package
|
168
|
+
"D106", # Missing docstring in public nested class
|
169
|
+
"D202", # D202 No blank lines allowed after function docstring
|
170
|
+
"D203", # We want to have blank line before class
|
171
|
+
"D212", # We want to require second line for multiline docstrings
|
172
|
+
]
|
173
|
+
extend-select = ["S", "E", "B", "A", "EM", "UP", "LOG", "G", "I", "D"]
|
{openepd-6.13.2 → openepd-6.15.0}/src/openepd/api/average_dataset/generic_estimate_sync_api.py
RENAMED
@@ -135,7 +135,8 @@ class GenericEstimateApi(BaseApiMethodGroup):
|
|
135
135
|
"""
|
136
136
|
ge_id = ge.id
|
137
137
|
if not ge_id:
|
138
|
-
|
138
|
+
msg = "The ID must be set to edit a GenericEstimate."
|
139
|
+
raise ValueError(msg)
|
139
140
|
|
140
141
|
response = self._client.do_request(
|
141
142
|
"put",
|
@@ -90,7 +90,8 @@ class IndustryEpdApi(BaseApiMethodGroup):
|
|
90
90
|
"""
|
91
91
|
iepd_id = iepd.id
|
92
92
|
if not iepd_id:
|
93
|
-
|
93
|
+
msg = "The ID must be set to edit a IndustryEpd."
|
94
|
+
raise ValueError(msg)
|
94
95
|
|
95
96
|
response = self._client.do_request(
|
96
97
|
"put",
|
@@ -14,14 +14,15 @@
|
|
14
14
|
# limitations under the License.
|
15
15
|
#
|
16
16
|
__all__ = (
|
17
|
-
"
|
18
|
-
"
|
17
|
+
"USER_AGENT_DEFAULT",
|
18
|
+
"BaseApiMethodGroup",
|
19
19
|
"DoRequest",
|
20
|
+
"HttpStreamReader",
|
20
21
|
"RetryHandler",
|
21
|
-
"
|
22
|
-
"USER_AGENT_DEFAULT",
|
22
|
+
"SyncHttpClient",
|
23
23
|
)
|
24
24
|
|
25
|
+
from collections.abc import Callable
|
25
26
|
import datetime
|
26
27
|
from functools import partial, wraps
|
27
28
|
from io import IOBase
|
@@ -29,7 +30,7 @@ import logging
|
|
29
30
|
import random
|
30
31
|
import shutil
|
31
32
|
import time
|
32
|
-
from typing import IO, Any, BinaryIO,
|
33
|
+
from typing import IO, Any, BinaryIO, Final, NamedTuple
|
33
34
|
|
34
35
|
import requests
|
35
36
|
from requests import PreparedRequest, Response, Session, Timeout
|
@@ -180,7 +181,7 @@ class SyncHttpClient:
|
|
180
181
|
self._throttler = Throttler(rate_per_sec=requests_per_sec)
|
181
182
|
self._throttle_retry_timeout: float = (
|
182
183
|
float(throttle_retry_timeout)
|
183
|
-
if isinstance(throttle_retry_timeout,
|
184
|
+
if isinstance(throttle_retry_timeout, float | int)
|
184
185
|
else throttle_retry_timeout.total_seconds()
|
185
186
|
)
|
186
187
|
self.user_agent = user_agent
|
@@ -396,7 +397,8 @@ class SyncHttpClient:
|
|
396
397
|
|
397
398
|
response.raise_for_status()
|
398
399
|
# This can't be handled by static checker because of the dynamic nature of the raise_for_status method
|
399
|
-
|
400
|
+
msg = "This line should never be reached"
|
401
|
+
raise RuntimeError(msg)
|
400
402
|
|
401
403
|
def _get_url_for_request(self, path_or_url: str) -> str:
|
402
404
|
"""
|
@@ -451,7 +453,7 @@ class SyncHttpClient:
|
|
451
453
|
exception = e
|
452
454
|
|
453
455
|
if exception or response.status_code == requests_codes.service_unavailable:
|
454
|
-
secs = random.randint(60, 60 * 5)
|
456
|
+
secs = random.randint(60, 60 * 5) # noqa: S311
|
455
457
|
logger.warning(
|
456
458
|
"%s %s is unavailable. Attempts left: %s. Waiting %s seconds...", method, url, attempts, secs
|
457
459
|
)
|
@@ -13,12 +13,12 @@
|
|
13
13
|
# See the License for the specific language governing permissions and
|
14
14
|
# limitations under the License.
|
15
15
|
#
|
16
|
-
from collections.abc import Iterable
|
16
|
+
from collections.abc import Callable, Iterable, Iterator
|
17
17
|
from contextlib import contextmanager
|
18
18
|
from datetime import datetime, timedelta
|
19
19
|
import threading
|
20
20
|
from time import sleep
|
21
|
-
from typing import
|
21
|
+
from typing import Generic, cast
|
22
22
|
|
23
23
|
from requests import Response
|
24
24
|
|
@@ -126,20 +126,26 @@ class StreamingListResponse(Iterable[TOpenEpdObject], Generic[TOpenEpdObject]):
|
|
126
126
|
:return: list of items on the page
|
127
127
|
"""
|
128
128
|
if page_num <= 0:
|
129
|
-
|
129
|
+
msg = "Page number must be positive"
|
130
|
+
raise ValueError(msg)
|
130
131
|
if self.__current_page != page_num or force_reload:
|
131
132
|
self.__recent_response = self.__fetch_handler(page_num, self.__page_size)
|
132
133
|
self.__current_page = page_num
|
133
134
|
if self.__recent_response is None:
|
134
|
-
|
135
|
+
msg = "Response is empty, this should not happen, check if fetch_handler is compatible"
|
136
|
+
raise RuntimeError(msg)
|
135
137
|
if self.__recent_response.payload is None:
|
136
|
-
|
138
|
+
msg = "Response does not contain payload"
|
139
|
+
raise ValueError(msg)
|
137
140
|
if not isinstance(self.__recent_response.payload, list):
|
138
|
-
|
141
|
+
msg = "Response does not contain a list"
|
142
|
+
raise ValueError(msg)
|
139
143
|
if self.__recent_response.meta is None:
|
140
|
-
|
144
|
+
msg = "Response does not contain meta"
|
145
|
+
raise ValueError(msg)
|
141
146
|
if not isinstance(self.__recent_response.meta, PagingMetaMixin):
|
142
|
-
|
147
|
+
msg = "Response does not contain paging meta"
|
148
|
+
raise ValueError(msg)
|
143
149
|
return self.__recent_response.payload
|
144
150
|
|
145
151
|
def get_paging_meta(self) -> PagingMeta:
|
@@ -152,7 +158,8 @@ class StreamingListResponse(Iterable[TOpenEpdObject], Generic[TOpenEpdObject]):
|
|
152
158
|
"""
|
153
159
|
paging_meta = cast(PagingMetaMixin, self.get_meta()).paging
|
154
160
|
if paging_meta is None:
|
155
|
-
|
161
|
+
msg = "Response does not contain paging meta"
|
162
|
+
raise ValueError(msg)
|
156
163
|
return paging_meta
|
157
164
|
|
158
165
|
def get_meta(self) -> MetaCollectionDto:
|
@@ -206,8 +213,7 @@ class StreamingListResponse(Iterable[TOpenEpdObject], Generic[TOpenEpdObject]):
|
|
206
213
|
self.goto_page(start_from_page)
|
207
214
|
while True:
|
208
215
|
items = self.goto_page(self.current_page)
|
209
|
-
|
210
|
-
yield x
|
216
|
+
yield from items
|
211
217
|
if not self.has_next_page():
|
212
218
|
return # no more pages
|
213
219
|
else:
|
@@ -82,7 +82,7 @@ class MetaCollectionDto(BaseOpenEpdApiModel, pyd_generics.GenericModel, Generic[
|
|
82
82
|
|
83
83
|
It is generified by the extension type of similar structure (key-dict value), which should not be a part of OpenEPD
|
84
84
|
spec. This allows to add custom meta objects to the response, which would be ignored by OpenEPD implementors.
|
85
|
-
"""
|
85
|
+
""" # noqa: D404
|
86
86
|
|
87
87
|
ext: TMetaExtension | None = None
|
88
88
|
|
@@ -173,7 +173,8 @@ class EpdApi(BaseApiMethodGroup):
|
|
173
173
|
"""
|
174
174
|
epd_id = epd.id
|
175
175
|
if not epd_id:
|
176
|
-
|
176
|
+
msg = "The EPD ID must be set to edit an EPD."
|
177
|
+
raise ValueError(msg)
|
177
178
|
response = self._client.do_request(
|
178
179
|
"put",
|
179
180
|
f"/epds/{encode_path_param(epd_id)}",
|
@@ -14,8 +14,9 @@
|
|
14
14
|
# limitations under the License.
|
15
15
|
#
|
16
16
|
import abc
|
17
|
+
from collections.abc import Callable, Iterator, Sequence
|
17
18
|
import csv
|
18
|
-
from typing import IO,
|
19
|
+
from typing import IO, Self
|
19
20
|
|
20
21
|
from openepd.bundle.model import AssetInfo, AssetType, BundleManifest, RelType
|
21
22
|
from openepd.model.base import TOpenEpdObject
|
@@ -70,7 +71,7 @@ class BaseBundleReader(BundleMixin, metaclass=abc.ABCMeta):
|
|
70
71
|
def __enter__(self) -> Self:
|
71
72
|
return self
|
72
73
|
|
73
|
-
def __exit__(self, type, value, traceback):
|
74
|
+
def __exit__(self, type, value, traceback): # noqa: A002
|
74
75
|
self.close()
|
75
76
|
|
76
77
|
@abc.abstractmethod
|
@@ -133,7 +134,7 @@ class BaseBundleReader(BundleMixin, metaclass=abc.ABCMeta):
|
|
133
134
|
pass
|
134
135
|
|
135
136
|
@abc.abstractmethod
|
136
|
-
def read_object_asset(self, obj_class:
|
137
|
+
def read_object_asset(self, obj_class: type[TOpenEpdObject], asset_ref: AssetRef) -> TOpenEpdObject:
|
137
138
|
"""Read an object asset by given reference."""
|
138
139
|
pass
|
139
140
|
|
@@ -171,7 +172,7 @@ class BaseBundleWriter(BundleMixin, metaclass=abc.ABCMeta):
|
|
171
172
|
def __enter__(self) -> Self:
|
172
173
|
return self
|
173
174
|
|
174
|
-
def __exit__(self, type, value, traceback):
|
175
|
+
def __exit__(self, type, value, traceback): # noqa: A002
|
175
176
|
self.close()
|
176
177
|
|
177
178
|
@abc.abstractmethod
|
@@ -13,10 +13,11 @@
|
|
13
13
|
# See the License for the specific language governing permissions and
|
14
14
|
# limitations under the License.
|
15
15
|
#
|
16
|
+
from collections.abc import Callable, Iterator, Sequence
|
16
17
|
import csv
|
17
18
|
import io
|
18
19
|
from os import PathLike
|
19
|
-
from typing import IO,
|
20
|
+
from typing import IO, cast
|
20
21
|
import zipfile
|
21
22
|
|
22
23
|
from openepd.bundle.base import AssetFilter, AssetRef, BaseBundleReader
|
@@ -88,7 +89,8 @@ class DefaultBundleReader(BaseBundleReader):
|
|
88
89
|
with self._bundle_archive.open("toc", "r") as toc_stream:
|
89
90
|
toc_reader = csv.DictReader(io.TextIOWrapper(toc_stream, encoding="utf-8"), dialect="toc")
|
90
91
|
if not toc_reader.fieldnames or len(toc_reader.fieldnames) < len(self._TOC_FIELDS):
|
91
|
-
|
92
|
+
msg = "The bundle file is not valid. TOC reading error: wrong number of fields"
|
93
|
+
raise ValueError(msg)
|
92
94
|
|
93
95
|
def root_assets_iter(
|
94
96
|
self,
|
@@ -141,17 +143,21 @@ class DefaultBundleReader(BaseBundleReader):
|
|
141
143
|
"""Read the blob asset."""
|
142
144
|
asset = self.get_asset_by_ref(asset_ref)
|
143
145
|
if asset is None:
|
144
|
-
|
146
|
+
msg = "Asset not found"
|
147
|
+
raise ValueError(msg)
|
145
148
|
return self._bundle_archive.open(asset.ref, "r")
|
146
149
|
|
147
|
-
def read_object_asset(self, obj_class:
|
150
|
+
def read_object_asset(self, obj_class: type[TOpenEpdObject], asset_ref: AssetRef) -> TOpenEpdObject:
|
148
151
|
"""Read the object asset."""
|
149
152
|
asset = self.get_asset_by_ref(asset_ref)
|
150
153
|
if asset is None:
|
151
|
-
|
154
|
+
msg = "Asset not found"
|
155
|
+
raise ValueError(msg)
|
152
156
|
if obj_class.get_asset_type() is None:
|
153
|
-
|
157
|
+
msg = f"Target object {obj_class.__name__} is not supported asset"
|
158
|
+
raise ValueError(msg)
|
154
159
|
if asset.type != obj_class.get_asset_type():
|
155
|
-
|
160
|
+
msg = f"Asset type mismatch. Expected {obj_class.get_asset_type()}, got {asset.type}"
|
161
|
+
raise ValueError(msg)
|
156
162
|
with self._bundle_archive.open(asset.ref, "r") as asset_stream:
|
157
163
|
return obj_class.parse_raw(asset_stream.read())
|
@@ -31,8 +31,9 @@ class DefaultBundleWriter(BaseBundleWriter):
|
|
31
31
|
"""Default bundle writer implementation. Writes the bundle to a ZIP file."""
|
32
32
|
|
33
33
|
def __init__(self, bundle_file: str | PathLike | IO[bytes], comment: str | None = None):
|
34
|
-
if isinstance(bundle_file,
|
35
|
-
|
34
|
+
if isinstance(bundle_file, PathLike | str) and Path(bundle_file).exists():
|
35
|
+
msg = "Amending existing files is not supported yet."
|
36
|
+
raise ValueError(msg)
|
36
37
|
self._bundle_archive = zipfile.ZipFile(bundle_file, mode="w")
|
37
38
|
self.__manifest = BundleManifest(
|
38
39
|
format="openEPD Bundle/1.0",
|
@@ -94,7 +95,8 @@ class DefaultBundleWriter(BaseBundleWriter):
|
|
94
95
|
"""Write an object asset to the bundle. Object means subclass of BaseOpenEpdSchem."""
|
95
96
|
asset_type_str = obj.get_asset_type()
|
96
97
|
if asset_type_str is None:
|
97
|
-
|
98
|
+
msg = f"Object {obj} does not have a valid asset type and can't be written to a bundle."
|
99
|
+
raise ValueError(msg)
|
98
100
|
asset_type = AssetType(asset_type_str)
|
99
101
|
rel_ref_str = self._asset_ref_to_str(rel_asset) if rel_asset is not None else None
|
100
102
|
ref_str = self.__generate_entry_name(
|
@@ -133,14 +135,16 @@ class DefaultBundleWriter(BaseBundleWriter):
|
|
133
135
|
|
134
136
|
def __register_entry(self, asset_info: AssetInfo):
|
135
137
|
if asset_info.ref in self.__added_entries:
|
136
|
-
|
138
|
+
msg = f"Asset {asset_info.ref} already exists in the bundle."
|
139
|
+
raise ValueError(msg)
|
137
140
|
self._toc_writer.writerow(asset_info.dict(exclude_unset=True, exclude_none=True))
|
138
141
|
self.__added_entries.add(asset_info.ref)
|
139
142
|
type_counter = self.__manifest.assets.count_by_type.get(asset_info.type, 0) + 1
|
140
143
|
self.__manifest.assets.count_by_type[asset_info.type] = type_counter
|
141
144
|
self.__manifest.assets.total_count += 1
|
142
145
|
if asset_info.size is None:
|
143
|
-
|
146
|
+
msg = "Size of asset is not set."
|
147
|
+
raise ValueError(msg)
|
144
148
|
self.__manifest.assets.total_size += asset_info.size
|
145
149
|
|
146
150
|
def __generate_entry_name(self, asset_type: str, extension: str | None = None, file_name: str | None = None) -> str:
|
@@ -157,7 +161,8 @@ class DefaultBundleWriter(BaseBundleWriter):
|
|
157
161
|
info = self._bundle_archive.getinfo(f"{asset_type}/")
|
158
162
|
if info.is_dir():
|
159
163
|
return
|
160
|
-
|
164
|
+
msg = f"Object with name {asset_type} already exists in the bundle."
|
165
|
+
raise ValueError(msg)
|
161
166
|
except KeyError:
|
162
167
|
self._bundle_archive.mkdir(str(asset_type))
|
163
168
|
|
@@ -1169,7 +1169,7 @@ def is_m49_code(to_check: str) -> bool:
|
|
1169
1169
|
:param to_check: any string
|
1170
1170
|
:return: `True` if passed string is M49 code, `False` otherwise
|
1171
1171
|
"""
|
1172
|
-
warnings.warn("Use m49.utils.is_m49_code instead.", DeprecationWarning)
|
1172
|
+
warnings.warn("Use m49.utils.is_m49_code instead.", DeprecationWarning, stacklevel=2)
|
1173
1173
|
from . import utils
|
1174
1174
|
|
1175
1175
|
return utils.is_m49_code(to_check)
|
@@ -14,15 +14,15 @@
|
|
14
14
|
# limitations under the License.
|
15
15
|
#
|
16
16
|
__all__ = [
|
17
|
+
"is_m49_code",
|
17
18
|
"iso_to_m49",
|
18
19
|
"m49_to_iso",
|
19
|
-
"
|
20
|
+
"m49_to_openepd",
|
20
21
|
"m49_to_region_and_country_names",
|
21
22
|
"openepd_to_m49",
|
22
|
-
"
|
23
|
-
"is_m49_code",
|
23
|
+
"region_and_country_names_to_m49",
|
24
24
|
]
|
25
|
-
from
|
25
|
+
from collections.abc import Collection
|
26
26
|
|
27
27
|
from openepd.m49.const import (
|
28
28
|
COUNTRY_VERBOSE_NAME_TO_M49,
|
@@ -54,7 +54,8 @@ def iso_to_m49(regions: Collection[str]) -> set[str]:
|
|
54
54
|
if m49_code:
|
55
55
|
result.add(m49_code)
|
56
56
|
else:
|
57
|
-
|
57
|
+
msg = f"Country code '{code}' not found in M49 region codes."
|
58
|
+
raise ValueError(msg)
|
58
59
|
|
59
60
|
return result
|
60
61
|
|
@@ -77,7 +78,8 @@ def m49_to_iso(regions: Collection[str]) -> set[str]:
|
|
77
78
|
if iso_code:
|
78
79
|
result.add(iso_code)
|
79
80
|
else:
|
80
|
-
|
81
|
+
msg = f"Region code '{code}' not found in ISO3166."
|
82
|
+
raise ValueError(msg)
|
81
83
|
|
82
84
|
return result
|
83
85
|
|
@@ -99,7 +101,8 @@ def region_and_country_names_to_m49(regions: Collection[str]) -> set[str]:
|
|
99
101
|
for name in regions:
|
100
102
|
m49_code = REGION_VERBOSE_NAME_TO_M49.get(name.title()) or COUNTRY_VERBOSE_NAME_TO_M49.get(name.title())
|
101
103
|
if not m49_code:
|
102
|
-
|
104
|
+
msg = f"Region or country name '{name}' not found in M49 region codes."
|
105
|
+
raise ValueError(msg)
|
103
106
|
result.add(m49_code)
|
104
107
|
|
105
108
|
return result
|
@@ -120,7 +123,8 @@ def m49_to_region_and_country_names(regions: Collection[str]) -> set[str]:
|
|
120
123
|
result = set()
|
121
124
|
for code in regions:
|
122
125
|
if code not in M49_TO_REGION_VERBOSE_NAME and code not in M49_TO_COUNTRY_VERBOSE_NAME:
|
123
|
-
|
126
|
+
msg = f"Region code '{code}' not found in M49 region codes."
|
127
|
+
raise ValueError(msg)
|
124
128
|
|
125
129
|
name = M49_TO_REGION_VERBOSE_NAME.get(code) or M49_TO_COUNTRY_VERBOSE_NAME.get(code, code)
|
126
130
|
result.add(name)
|
@@ -153,7 +157,8 @@ def openepd_to_m49(regions: Collection[str]) -> set[str]:
|
|
153
157
|
elif is_m49_code(region):
|
154
158
|
result.add(region)
|
155
159
|
else:
|
156
|
-
|
160
|
+
msg = f"Region '{region}' not found in ISO3166 or OpenEPD special regions."
|
161
|
+
raise ValueError(msg)
|
157
162
|
return result
|
158
163
|
|
159
164
|
|
@@ -185,7 +190,8 @@ def m49_to_openepd(regions: list[str]) -> set[str]:
|
|
185
190
|
if iso_code:
|
186
191
|
result.add(iso_code)
|
187
192
|
else:
|
188
|
-
|
193
|
+
msg = f"Region code '{code}' not found in ISO3166 or OpenEPD special regions."
|
194
|
+
raise ValueError(msg)
|
189
195
|
|
190
196
|
return result
|
191
197
|
|
@@ -14,9 +14,10 @@
|
|
14
14
|
# limitations under the License.
|
15
15
|
#
|
16
16
|
import abc
|
17
|
+
from collections.abc import Callable
|
17
18
|
from enum import StrEnum
|
18
19
|
import json
|
19
|
-
from typing import Any,
|
20
|
+
from typing import Any, ClassVar, Generic, Optional, TypeAlias, TypeVar
|
20
21
|
|
21
22
|
from cqd import open_xpd_uuid # type:ignore[import-untyped,ignore-not-found]
|
22
23
|
|
@@ -106,7 +107,7 @@ class BaseOpenEpdSchema(pyd.BaseModel):
|
|
106
107
|
return self.ext.get(key, default)
|
107
108
|
|
108
109
|
def get_typed_ext_field(
|
109
|
-
self, key: str, target_type:
|
110
|
+
self, key: str, target_type: type[TAnySerializable], default: TAnySerializable | None = None
|
110
111
|
) -> TAnySerializable:
|
111
112
|
"""
|
112
113
|
Get an extension field from the model and convert it to the target type.
|
@@ -120,13 +121,14 @@ class BaseOpenEpdSchema(pyd.BaseModel):
|
|
120
121
|
return target_type.parse_obj(value) # type: ignore[return-value]
|
121
122
|
elif isinstance(value, target_type):
|
122
123
|
return value
|
123
|
-
|
124
|
+
msg = f"Cannot convert {value} to {target_type}"
|
125
|
+
raise ValueError(msg)
|
124
126
|
|
125
|
-
def get_ext(self, ext_type:
|
127
|
+
def get_ext(self, ext_type: type["TOpenEpdExtension"]) -> Optional["TOpenEpdExtension"]:
|
126
128
|
"""Get an extension field from the model or None if it doesn't exist."""
|
127
129
|
return self.get_typed_ext_field(ext_type.get_extension_name(), ext_type, None)
|
128
130
|
|
129
|
-
def get_ext_or_empty(self, ext_type:
|
131
|
+
def get_ext_or_empty(self, ext_type: type["TOpenEpdExtension"]) -> "TOpenEpdExtension":
|
130
132
|
"""Get an extension field from the model or an empty instance if it doesn't exist."""
|
131
133
|
return self.get_typed_ext_field(ext_type.get_extension_name(), ext_type, ext_type.construct(**{}))
|
132
134
|
|
@@ -174,7 +176,7 @@ class OpenEpdExtension(BaseOpenEpdSchema, metaclass=abc.ABCMeta):
|
|
174
176
|
|
175
177
|
TOpenEpdExtension = TypeVar("TOpenEpdExtension", bound=OpenEpdExtension)
|
176
178
|
TOpenEpdObject = TypeVar("TOpenEpdObject", bound=BaseOpenEpdSchema)
|
177
|
-
TOpenEpdObjectClass = TypeVar("TOpenEpdObjectClass", bound=
|
179
|
+
TOpenEpdObjectClass = TypeVar("TOpenEpdObjectClass", bound=type[BaseOpenEpdSchema])
|
178
180
|
|
179
181
|
|
180
182
|
class RootDocument(abc.ABC, BaseOpenEpdSchema):
|
@@ -218,23 +220,24 @@ class BaseDocumentFactory(Generic[TRootDocument]):
|
|
218
220
|
"""Create a document from a dictionary."""
|
219
221
|
doctype: str | None = data.get("doctype")
|
220
222
|
if doctype is None:
|
221
|
-
|
223
|
+
msg = "Doctype not found in the data."
|
224
|
+
raise ValueError(msg)
|
222
225
|
if doctype.lower() != cls.DOCTYPE_CONSTRAINT.lower():
|
223
|
-
|
224
|
-
|
225
|
-
)
|
226
|
+
msg = f"Document type {doctype} not supported. This factory supports {cls.DOCTYPE_CONSTRAINT} only."
|
227
|
+
raise ValueError(msg)
|
226
228
|
version = Version.parse_version(data.get(OPENEPD_VERSION_FIELD, ""))
|
227
229
|
for x, doc_cls in cls.VERSION_MAP.items():
|
228
230
|
if x.major == version.major:
|
229
231
|
if version.minor <= x.minor:
|
230
232
|
return doc_cls(**data)
|
231
233
|
else:
|
232
|
-
|
233
|
-
f"Unsupported version: {version}. "
|
234
|
-
f"The highest supported version from branch {x.major}.x is {x}"
|
234
|
+
msg = (
|
235
|
+
f"Unsupported version: {version}. The highest supported version from branch {x.major}.x is {x}"
|
235
236
|
)
|
237
|
+
raise ValueError(msg)
|
236
238
|
supported_versions = ", ".join(f"{v.major}.x" for v in cls.VERSION_MAP.keys())
|
237
|
-
|
239
|
+
msg = f"Version {version} is not supported. Supported versions are: {supported_versions}"
|
240
|
+
raise ValueError(msg)
|
238
241
|
|
239
242
|
|
240
243
|
class OpenXpdUUID(str):
|
@@ -252,7 +255,8 @@ class OpenXpdUUID(str):
|
|
252
255
|
open_xpd_uuid.validate(open_xpd_uuid.sanitize(str(v)))
|
253
256
|
return v
|
254
257
|
except open_xpd_uuid.GuidValidationError as e:
|
255
|
-
|
258
|
+
msg = "Invalid format"
|
259
|
+
raise ValueError(msg) from e
|
256
260
|
|
257
261
|
@classmethod
|
258
262
|
def __get_validators__(cls):
|