openepd 7.1.0__py3-none-any.whl → 7.3.0__py3-none-any.whl

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 (78) hide show
  1. openepd/__version__.py +1 -1
  2. openepd/api/average_dataset/generic_estimate_sync_api.py +2 -1
  3. openepd/api/average_dataset/industry_epd_sync_api.py +2 -1
  4. openepd/api/base_sync_client.py +10 -8
  5. openepd/api/common.py +17 -11
  6. openepd/api/dto/common.py +1 -1
  7. openepd/api/epd/sync_api.py +2 -1
  8. openepd/api/org/__init__.py +15 -0
  9. openepd/api/org/sync_api.py +79 -0
  10. openepd/api/pcr/sync_api.py +35 -0
  11. openepd/api/plant/__init__.py +15 -0
  12. openepd/api/plant/sync_api.py +79 -0
  13. openepd/api/standard/__init__.py +15 -0
  14. openepd/api/standard/sync_api.py +79 -0
  15. openepd/api/sync_client.py +27 -0
  16. openepd/bundle/base.py +47 -6
  17. openepd/bundle/model.py +1 -0
  18. openepd/bundle/reader.py +35 -10
  19. openepd/bundle/writer.py +21 -12
  20. openepd/m49/__init__.py +2 -0
  21. openepd/m49/const.py +1 -1
  22. openepd/m49/utils.py +16 -10
  23. openepd/model/base.py +20 -15
  24. openepd/model/common.py +10 -5
  25. openepd/model/declaration.py +2 -2
  26. openepd/model/epd.py +2 -1
  27. openepd/model/factory.py +5 -3
  28. openepd/model/generic_estimate.py +4 -0
  29. openepd/model/lcia.py +10 -10
  30. openepd/model/org.py +14 -7
  31. openepd/model/pcr.py +2 -2
  32. openepd/model/specs/__init__.py +37 -0
  33. openepd/model/specs/asphalt.py +3 -3
  34. openepd/model/specs/base.py +2 -1
  35. openepd/model/specs/enums.py +9 -1
  36. openepd/model/specs/range/__init__.py +5 -3
  37. openepd/model/specs/range/accessories.py +1 -1
  38. openepd/model/specs/range/aluminium.py +1 -1
  39. openepd/model/specs/range/cladding.py +10 -10
  40. openepd/model/specs/range/cmu.py +0 -2
  41. openepd/model/specs/range/concrete.py +25 -2
  42. openepd/model/specs/range/conveying_equipment.py +2 -2
  43. openepd/model/specs/range/electrical.py +18 -18
  44. openepd/model/specs/range/electrical_transmission_and_distribution_equipment.py +1 -1
  45. openepd/model/specs/range/exterior_improvements.py +47 -0
  46. openepd/model/specs/range/finishes.py +19 -40
  47. openepd/model/specs/range/fire_and_smoke_protection.py +3 -3
  48. openepd/model/specs/range/furnishings.py +7 -7
  49. openepd/model/specs/range/manufacturing_inputs.py +17 -5
  50. openepd/model/specs/range/masonry.py +1 -1
  51. openepd/model/specs/range/mechanical.py +6 -6
  52. openepd/model/specs/range/mixins/__init__.py +15 -0
  53. openepd/model/specs/range/mixins/access_flooring_mixin.py +43 -0
  54. openepd/model/specs/range/network_infrastructure.py +3 -3
  55. openepd/model/specs/range/openings.py +17 -17
  56. openepd/model/specs/range/other_materials.py +4 -4
  57. openepd/model/specs/range/plumbing.py +5 -5
  58. openepd/model/specs/range/precast_concrete.py +2 -2
  59. openepd/model/specs/range/steel.py +18 -9
  60. openepd/model/specs/range/thermal_moisture_protection.py +12 -12
  61. openepd/model/specs/range/wood.py +4 -6
  62. openepd/model/specs/singular/__init__.py +119 -2
  63. openepd/model/specs/singular/aluminium.py +2 -1
  64. openepd/model/specs/singular/concrete.py +25 -1
  65. openepd/model/specs/singular/deprecated/__init__.py +1 -1
  66. openepd/model/specs/singular/exterior_improvements.py +47 -0
  67. openepd/model/specs/singular/finishes.py +3 -59
  68. openepd/model/specs/singular/manufacturing_inputs.py +13 -1
  69. openepd/model/specs/singular/mixins/access_flooring_mixin.py +55 -0
  70. openepd/model/specs/singular/steel.py +10 -2
  71. openepd/model/validation/common.py +10 -6
  72. openepd/model/validation/enum.py +4 -2
  73. openepd/model/validation/quantity.py +13 -6
  74. openepd/model/versioning.py +8 -6
  75. {openepd-7.1.0.dist-info → openepd-7.3.0.dist-info}/METADATA +23 -13
  76. {openepd-7.1.0.dist-info → openepd-7.3.0.dist-info}/RECORD +78 -67
  77. {openepd-7.1.0.dist-info → openepd-7.3.0.dist-info}/WHEEL +1 -1
  78. {openepd-7.1.0.dist-info → openepd-7.3.0.dist-info}/LICENSE +0 -0
@@ -17,10 +17,6 @@ import pydantic
17
17
 
18
18
  from openepd.model.specs.base import BaseOpenEpdHierarchicalSpec
19
19
  from openepd.model.specs.enums import (
20
- AccessFlooringCoreMaterial,
21
- AccessFlooringFinishMaterial,
22
- AccessFlooringSeismicRating,
23
- AccessFlooringStringers,
24
20
  AllFabrication,
25
21
  CarpetFormFactor,
26
22
  CarpetIntendedApplication,
@@ -44,13 +40,12 @@ from openepd.model.specs.enums import (
44
40
  WoodFlooringTimberSpecies,
45
41
  )
46
42
  from openepd.model.specs.singular.common import HasForestPracticesCertifiers
43
+ from openepd.model.specs.singular.mixins.access_flooring_mixin import AccessFlooringMixin
47
44
  from openepd.model.validation.quantity import (
48
45
  AreaPerVolumeStr,
49
- ForceNStr,
50
46
  GwpKgCo2eStr,
51
47
  LengthMmStr,
52
48
  LengthMStr,
53
- PressureMPaStr,
54
49
  RFactorStr,
55
50
  YarnWeightStr,
56
51
  validate_quantity_ge_factory,
@@ -59,7 +54,7 @@ from openepd.model.validation.quantity import (
59
54
  )
60
55
 
61
56
 
62
- class AccessFlooringV1(BaseOpenEpdHierarchicalSpec):
57
+ class AccessFlooringV1(BaseOpenEpdHierarchicalSpec, AccessFlooringMixin):
63
58
  """
64
59
  Elevated floor system built on top of concrete slab surface.
65
60
 
@@ -69,57 +64,6 @@ class AccessFlooringV1(BaseOpenEpdHierarchicalSpec):
69
64
 
70
65
  _EXT_VERSION = "1.0"
71
66
 
72
- # Own fields:
73
- core_material: AccessFlooringCoreMaterial | None = pydantic.Field(
74
- default=None, description="", examples=["Cementitious"]
75
- )
76
- finish_material: AccessFlooringFinishMaterial | None = pydantic.Field(
77
- default=None, description="", examples=["Linoleum"]
78
- )
79
- stringers: AccessFlooringStringers | None = pydantic.Field(default=None, description="", examples=["Standard"])
80
- seismic_rating: AccessFlooringSeismicRating | None = pydantic.Field(
81
- default=None, description="", examples=["Type 0"]
82
- )
83
- magnetically_attached_finish: bool | None = pydantic.Field(
84
- default=None,
85
- description="",
86
- examples=[True],
87
- )
88
- permanent_finish: bool | None = pydantic.Field(
89
- default=None,
90
- description="",
91
- examples=[True],
92
- )
93
- drylay: bool | None = pydantic.Field(
94
- default=None,
95
- description="",
96
- examples=[True],
97
- )
98
- adjustable_height: bool | None = pydantic.Field(
99
- default=None,
100
- description="",
101
- examples=[True],
102
- )
103
- fixed_height: bool | None = pydantic.Field(
104
- default=None,
105
- description="",
106
- examples=[True],
107
- )
108
- finished_floor_height: LengthMmStr | None = pydantic.Field(default=None, description="", examples=["1 m"])
109
- panel_thickness: LengthMmStr | None = pydantic.Field(default=None, description="", examples=["1 m"])
110
- concentrated_load: PressureMPaStr | None = pydantic.Field(default=None, description="", examples=["1 MPa"])
111
- uniform_load: PressureMPaStr | None = pydantic.Field(default=None, description="", examples=["1 MPa"])
112
- rolling_load_10_pass: ForceNStr | None = pydantic.Field(default=None, description="", examples=["1 N"])
113
- rolling_load_10000_pass: ForceNStr | None = pydantic.Field(default=None, description="", examples=["1 N"])
114
-
115
- @pydantic.field_validator("rolling_load_10_pass", mode="before", check_fields=False)
116
- def _access_flooring_rolling_load_10_pass_is_quantity_validator(cls, value):
117
- return validate_quantity_unit_factory("N")(cls, value)
118
-
119
- @pydantic.field_validator("rolling_load_10000_pass", mode="before", check_fields=False)
120
- def _access_flooring_rolling_load_10000_pass_is_quantity_validator(cls, value):
121
- return validate_quantity_unit_factory("N")(cls, value)
122
-
123
67
 
124
68
  class CarpetV1(BaseOpenEpdHierarchicalSpec):
125
69
  """Textile Floor Coverings."""
@@ -419,7 +363,7 @@ class BackingAndUnderlayV1(BaseOpenEpdHierarchicalSpec):
419
363
  class CementBoardV1(BaseOpenEpdHierarchicalSpec):
420
364
  """Hard cementitious boards, typically used as a tile backer."""
421
365
 
422
- _EXT_VERSION = "1.0"
366
+ _EXT_VERSION = "1.1"
423
367
 
424
368
  # Own fields:
425
369
  thickness: CementBoardThickness | None = pydantic.Field(
@@ -28,6 +28,7 @@ from openepd.model.specs.enums import (
28
28
  MasonryCementAstmC91Type,
29
29
  TextilesFabricType,
30
30
  )
31
+ from openepd.model.specs.singular.mixins.access_flooring_mixin import AccessFlooringMixin
31
32
 
32
33
 
33
34
  class CementV1(BaseOpenEpdHierarchicalSpec):
@@ -141,6 +142,16 @@ class TextilesV1(BaseOpenEpdHierarchicalSpec):
141
142
  fabric_type: list[TextilesFabricType] | None = pydantic.Field(default=None, description="", examples=[["Leather"]])
142
143
 
143
144
 
145
+ class AccessFlooringPanelsV1(BaseOpenEpdHierarchicalSpec, AccessFlooringMixin):
146
+ """
147
+ Part of an access floor system.
148
+
149
+ Panels are laid on top of an access floor pedestal, creating a finish floor.
150
+ """
151
+
152
+ _EXT_VERSION = "1.0"
153
+
154
+
144
155
  class ManufacturingInputsV1(BaseOpenEpdHierarchicalSpec):
145
156
  """
146
157
  Manufacturing inputs.
@@ -149,7 +160,7 @@ class ManufacturingInputsV1(BaseOpenEpdHierarchicalSpec):
149
160
  a construction.
150
161
  """
151
162
 
152
- _EXT_VERSION = "1.0"
163
+ _EXT_VERSION = "1.1"
153
164
 
154
165
  # Nested specs:
155
166
  AccessFlooringPedestals: AccessFlooringPedestalsV1 | None = None
@@ -158,3 +169,4 @@ class ManufacturingInputsV1(BaseOpenEpdHierarchicalSpec):
158
169
  CementitiousMaterials: CementitiousMaterialsV1 | None = None
159
170
  ConcreteAdmixtures: ConcreteAdmixturesV1 | None = None
160
171
  Textiles: TextilesV1 | None = None
172
+ AccessFlooringPanels: AccessFlooringPanelsV1 | None = None
@@ -0,0 +1,55 @@
1
+ #
2
+ # Copyright 2025 by C Change Labs Inc. www.c-change-labs.com
3
+ #
4
+ # Licensed under the Apache License, Version 2.0 (the "License");
5
+ # you may not use this file except in compliance with the License.
6
+ # You may obtain a copy of the License at
7
+ #
8
+ # http://www.apache.org/licenses/LICENSE-2.0
9
+ #
10
+ # Unless required by applicable law or agreed to in writing, software
11
+ # distributed under the License is distributed on an "AS IS" BASIS,
12
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ # See the License for the specific language governing permissions and
14
+ # limitations under the License.
15
+ #
16
+ import pydantic as pyd
17
+
18
+ from openepd.model.specs.base import BaseOpenEpdSpec
19
+ from openepd.model.specs.enums import (
20
+ AccessFlooringCoreMaterial,
21
+ AccessFlooringFinishMaterial,
22
+ AccessFlooringSeismicRating,
23
+ AccessFlooringStringers,
24
+ )
25
+ from openepd.model.validation.quantity import ForceNStr, LengthMmStr, PressureMPaStr, validate_quantity_unit_factory
26
+
27
+
28
+ class AccessFlooringMixin(BaseOpenEpdSpec):
29
+ core_material: AccessFlooringCoreMaterial | None = pyd.Field(
30
+ default=None, description="", examples=["Cementitious"]
31
+ )
32
+ finish_material: AccessFlooringFinishMaterial | None = pyd.Field(
33
+ default=None, description="", examples=["Linoleum"]
34
+ )
35
+ stringers: AccessFlooringStringers | None = pyd.Field(default=None, description="", examples=["Standard"])
36
+ seismic_rating: AccessFlooringSeismicRating | None = pyd.Field(default=None, description="", examples=["Type 0"])
37
+ magnetically_attached_finish: bool | None = pyd.Field(default=None, description="", examples=[True])
38
+ permanent_finish: bool | None = pyd.Field(default=None, description="", examples=[True])
39
+ drylay: bool | None = pyd.Field(default=None, description="", examples=[True])
40
+ adjustable_height: bool | None = pyd.Field(default=None, description="", examples=[True])
41
+ fixed_height: bool | None = pyd.Field(default=None, description="", examples=[True])
42
+ finished_floor_height: LengthMmStr | None = pyd.Field(default=None, description="", examples=["1 m"])
43
+ panel_thickness: LengthMmStr | None = pyd.Field(default=None, description="", examples=["1 m"])
44
+ concentrated_load: PressureMPaStr | None = pyd.Field(default=None, description="", examples=["1 MPa"])
45
+ uniform_load: PressureMPaStr | None = pyd.Field(default=None, description="", examples=["1 MPa"])
46
+ rolling_load_10_pass: ForceNStr | None = pyd.Field(default=None, description="", examples=["1 N"])
47
+ rolling_load_10000_pass: ForceNStr | None = pyd.Field(default=None, description="", examples=["1 N"])
48
+
49
+ @pyd.field_validator("rolling_load_10_pass", mode="before", check_fields=False)
50
+ def _access_flooring_rolling_load_10_pass_is_quantity_validator(cls, value):
51
+ return validate_quantity_unit_factory("N")(cls, value)
52
+
53
+ @pyd.field_validator("rolling_load_10000_pass", mode="before", check_fields=False)
54
+ def _access_flooring_rolling_load_10000_pass_is_quantity_validator(cls, value):
55
+ return validate_quantity_unit_factory("N")(cls, value)
@@ -89,7 +89,8 @@ class HotRolledSectionsV1(BaseOpenEpdHierarchicalSpec, SteelFabricatedMixin):
89
89
 
90
90
 
91
91
  class PlateSteelV1(BaseOpenEpdHierarchicalSpec, SteelFabricatedMixin):
92
- """Plate Steels.
92
+ """
93
+ Plate Steels.
93
94
 
94
95
  Flat hot-rolled steel, typically thicker than 'sheet', made by compressing multiple steel
95
96
  layers together into one.
@@ -260,10 +261,16 @@ class OtherSteelV1(BaseOpenEpdHierarchicalSpec):
260
261
  _EXT_VERSION = "1.0"
261
262
 
262
263
 
264
+ class CrudeSteelV1(BaseOpenEpdHierarchicalSpec):
265
+ """Steel ingots, billets, blooms, and slabs for use in manufacturing steel products."""
266
+
267
+ _EXT_VERSION = "1.0"
268
+
269
+
263
270
  class SteelV1(BaseOpenEpdHierarchicalSpec):
264
271
  """Broad category for construction materials made from steel and its alloys."""
265
272
 
266
- _EXT_VERSION = "1.1"
273
+ _EXT_VERSION = "1.2"
267
274
 
268
275
  # Own fields:
269
276
  yield_tensile_str: PressureMPaStr | None = pydantic.Field(
@@ -327,3 +334,4 @@ class SteelV1(BaseOpenEpdHierarchicalSpec):
327
334
  RebarSteel: RebarSteelV1 | None = None
328
335
  WireMeshSteel: WireMeshSteelV1 | None = None
329
336
  OtherSteel: OtherSteelV1 | None = None
337
+ CrudeSteel: CrudeSteelV1 | None = None
@@ -13,7 +13,8 @@
13
13
  # See the License for the specific language governing permissions and
14
14
  # limitations under the License.
15
15
  #
16
- from typing import Annotated, Any, Callable, Type, TypeAlias
16
+ from collections.abc import Callable
17
+ from typing import Annotated, Any, TypeAlias
17
18
 
18
19
  import pydantic
19
20
 
@@ -25,7 +26,8 @@ def together_validator(field1: str, field2: Any, values: dict[str, Any]) -> Any:
25
26
  value1 = values.get(field1)
26
27
  value2 = values.get(field2)
27
28
  if value1 is not None and value2 is None or value1 is None and value2 is not None:
28
- raise ValueError(f"Both or neither {field1} and {field2} days must be provided together")
29
+ msg = f"Both or neither {field1} and {field2} days must be provided together"
30
+ raise ValueError(msg)
29
31
 
30
32
 
31
33
  def validate_version_format(v: str) -> str:
@@ -36,17 +38,19 @@ def validate_version_format(v: str) -> str:
36
38
 
37
39
  def validate_version_compatibility(
38
40
  class_version_attribute_name: str,
39
- ) -> Callable[[Type, str], str]:
41
+ ) -> Callable[[type, str], str]:
40
42
  """Ensure that the object which is passed for parsing and validation is compatible with the class."""
41
43
 
42
44
  # we need closure to pass property name, since actual class will only be available in runtime
43
- def internal_validate_version_compatibility(cls: Type, v: str) -> str:
45
+ def internal_validate_version_compatibility(cls: type, v: str) -> str:
44
46
  if not hasattr(cls, class_version_attribute_name):
45
- raise ValueError(f"Class {cls} must declare a class var extension var named {class_version_attribute_name}")
47
+ msg = f"Class {cls} must declare a class var extension var named {class_version_attribute_name}"
48
+ raise ValueError(msg)
46
49
 
47
50
  class_version = getattr(cls, class_version_attribute_name)
48
51
  if Version.parse_version(v).major != Version.parse_version(class_version).major:
49
- raise ValueError(f"Extension version {v} does not match class version {class_version}")
52
+ msg = f"Extension version {v} does not match class version {class_version}"
53
+ raise ValueError(msg)
50
54
  return v
51
55
 
52
56
  return internal_validate_version_compatibility
@@ -13,7 +13,8 @@
13
13
  # See the License for the specific language governing permissions and
14
14
  # limitations under the License.
15
15
  #
16
- from typing import Any, Callable
16
+ from collections.abc import Callable
17
+ from typing import Any
17
18
 
18
19
  from openepd.model.common import EnumGroupingAware
19
20
 
@@ -35,7 +36,8 @@ def exclusive_groups_validator_factory(enum_type: type[EnumGroupingAware]) -> Ca
35
36
  for grouping in enum_type.get_groupings():
36
37
  matching_from_group = [v for v in (value or []) if v in grouping]
37
38
  if len(matching_from_group) > 1:
38
- raise ValueError(f"Values {', '.join(matching_from_group)} are not allowed together.")
39
+ msg = f"Values {', '.join(matching_from_group)} are not allowed together."
40
+ raise ValueError(msg)
39
41
 
40
42
  return value
41
43
 
@@ -14,12 +14,12 @@
14
14
  # limitations under the License.
15
15
  #
16
16
  from abc import ABC, abstractmethod
17
- from typing import TYPE_CHECKING, Any, Callable, ClassVar
17
+ from collections.abc import Callable
18
+ from typing import TYPE_CHECKING, Any, ClassVar, Self
18
19
 
19
20
  import pydantic
20
21
  from pydantic import ConfigDict
21
22
  import pydantic_core
22
- from typing_extensions import Self
23
23
 
24
24
  from openepd.model.base import BaseOpenEpdSchema
25
25
  from openepd.model.common import Amount, OpenEPDUnit, RangeAmount
@@ -59,8 +59,10 @@ class QuantityValidator(ABC):
59
59
  dimensionality: The dimensionality to validate against, like "kg"
60
60
  Returns:
61
61
  None if the value is valid, raises an error otherwise.
62
+
62
63
  Raises:
63
64
  ValueError: If the value is not valid.
65
+
64
66
  """
65
67
  pass
66
68
 
@@ -74,8 +76,10 @@ class QuantityValidator(ABC):
74
76
  min_value: The value to compare with, like "102.4 kg"
75
77
  Returns:
76
78
  None if the value is valid, raises an error otherwise.
79
+
77
80
  Raises:
78
81
  ValueError: If the value is not valid.
82
+
79
83
  """
80
84
  pass
81
85
 
@@ -89,8 +93,10 @@ class QuantityValidator(ABC):
89
93
  max_value: The value to compare with, like "0.4 kg"
90
94
  Returns:
91
95
  None if the value is valid, raises an error otherwise.
96
+
92
97
  Raises:
93
98
  ValueError: If the value is not valid.
99
+
94
100
  """
95
101
  pass
96
102
 
@@ -168,10 +174,10 @@ class QuantityStr(str):
168
174
  unit: ClassVar[str]
169
175
 
170
176
  @classmethod
171
- def _validate(cls, value: str) -> str:
177
+ def _validate(cls, value: str) -> Self:
172
178
  value = validate_quantity_unit_factory(cls.unit)(cls, value)
173
179
  value = validate_quantity_ge_factory(f"0 {cls.unit}")(cls, value)
174
- return value
180
+ return cls(value)
175
181
 
176
182
  @classmethod
177
183
  def __get_pydantic_core_schema__(
@@ -389,7 +395,8 @@ class WithDimensionalityMixin(BaseOpenEpdSchema):
389
395
  return self
390
396
 
391
397
  if self.unit is None:
392
- raise ValueError("`unit` is required for dimensionality-validated amounts")
398
+ msg = "`unit` is required for dimensionality-validated amounts"
399
+ raise ValueError(msg)
393
400
 
394
401
  validate_unit_factory(self.dimensionality_unit)(BaseOpenEpdSchema, self.unit)
395
402
  return self
@@ -490,7 +497,7 @@ class WithPressureMpaMixin(WithDimensionalityMixin):
490
497
 
491
498
 
492
499
  class AmountPressureMpa(Amount, WithPressureMpaMixin):
493
- """Length (mm)."""
500
+ """Pressure (MPa)."""
494
501
 
495
502
  pass
496
503
 
@@ -44,7 +44,8 @@ class Version(NamedTuple):
44
44
 
45
45
  @staticmethod
46
46
  def parse_version(version: str | None) -> "Version":
47
- """Parse the version of extension or the format.
47
+ """
48
+ Parse the version of extension or the format.
48
49
 
49
50
  Version is expected to be major.minor
50
51
 
@@ -56,9 +57,11 @@ class Version(NamedTuple):
56
57
  else:
57
58
  splits = []
58
59
  if len(splits) != 2:
59
- raise ValueError(f"Invalid version: {version}")
60
+ msg = f"Invalid version: {version}"
61
+ raise ValueError(msg)
60
62
  if not splits[0].isdigit() or not splits[1].isdigit():
61
- raise ValueError(f"Invalid version: {version}")
63
+ msg = f"Invalid version: {version}"
64
+ raise ValueError(msg)
62
65
  return Version(major=int(splits[0]), minor=int(splits[1]))
63
66
 
64
67
  def __str__(self) -> str:
@@ -115,9 +118,8 @@ class OpenEpdVersions(Version, ReprEnum):
115
118
  for x in cls:
116
119
  if x.value.major == branch:
117
120
  return x.value
118
- raise ValueError(
119
- f"No version {branch}.x is not supported. Supported versions are: {', '.join(str(x.value) for x in cls)}"
120
- )
121
+ msg = f"No version {branch}.x is not supported. Supported versions are: {', '.join(str(x.value) for x in cls)}"
122
+ raise ValueError(msg)
121
123
 
122
124
  @classmethod
123
125
  def get_current(cls) -> Version:
@@ -1,15 +1,14 @@
1
- Metadata-Version: 2.1
1
+ Metadata-Version: 2.3
2
2
  Name: openepd
3
- Version: 7.1.0
3
+ Version: 7.3.0
4
4
  Summary: Python library to work with OpenEPD format
5
- Home-page: https://github.com/cchangelabs/openepd
6
5
  License: Apache-2.0
7
6
  Author: C-Change Labs
8
7
  Author-email: support@c-change-labs.com
9
8
  Maintainer: C-Change Labs
10
9
  Maintainer-email: open-source@c-change-labs.com
11
10
  Requires-Python: >=3.11,<4.0
12
- Classifier: Development Status :: 3 - Alpha
11
+ Classifier: Development Status :: 4 - Beta
13
12
  Classifier: Intended Audience :: Developers
14
13
  Classifier: License :: OSI Approved :: Apache Software License
15
14
  Classifier: Operating System :: OS Independent
@@ -21,7 +20,7 @@ Classifier: Topic :: Software Development :: Libraries :: Python Modules
21
20
  Provides-Extra: api-client
22
21
  Requires-Dist: email-validator (>=1.3.1)
23
22
  Requires-Dist: idna (>=3.7)
24
- Requires-Dist: open-xpd-uuid (>=0.2.1,<0.3.0)
23
+ Requires-Dist: open-xpd-uuid (>=0.2.1,<2)
25
24
  Requires-Dist: openlocationcode (>=1.0.1)
26
25
  Requires-Dist: pydantic (>=2.0,<3)
27
26
  Requires-Dist: requests (>=2.0) ; extra == "api-client"
@@ -45,6 +44,24 @@ Description-Content-Type: text/markdown
45
44
 
46
45
  This library is a Python library to work with OpenEPD format.
47
46
 
47
+ > ⚠️ **Version Warning**
48
+ >
49
+ > This application is currently developed in **two major versions** in parallel:
50
+ >
51
+ > - **v6.x (>=6.0.0)** — Stable and production-ready. Maintains support for Pydantic v1 and v2 through a compatibility layer.
52
+ > - **v7.x (>=7.0.0)** — Public beta. Fully functional, with native support for Pydantic v2. Still experimental and may introduce breaking changes in **internal and integration interfaces**.
53
+ >
54
+ > ⚠️ No breaking changes are expected in the **public standard or data model**, only in internal APIs and integration points.
55
+ >
56
+ > Both versions currently offer the same set of features.
57
+ > We recommend using **v6** for most production use cases as the more mature and stable option.
58
+ > **v7** is suitable for production environments that can tolerate some level of interface instability and want to adopt the latest internals.
59
+ >
60
+ > 💡 Only the **latest version of v7** is guaranteed to contain all the features and updates from v6. Earlier v7 releases may lack some recent improvements.
61
+ >
62
+ > Once **v7 is promoted to stable**, all earlier **pre-stable (beta) v7 releases** will be **marked as yanked** to prevent accidental usage in production.
63
+ >
64
+
48
65
  ## About OpenEPD
49
66
 
50
67
  [openEPD](https://www.buildingtransparency.org/programs/openepd/) is an open data format for passing digital
@@ -60,17 +77,10 @@ including uniqueness of organizations/plants, precise PCR references, and dated
60
77
  The openEPD format is **extensible**. Standard extensions exist for concrete products, and for
61
78
  documenting supply-chain specific data.
62
79
 
63
- [Read More about OpenEPD format here](https://www.buildingtransparency.org/programs/openepd/).
80
+ [Read More about OpenEPD format here](https://www.open-epd-forum.org).
64
81
 
65
82
  ## Usage
66
83
 
67
- **❗ ATTENTION**: Pick the right version. The cornerstone of this library models package representing openEPD models.
68
- Models are defined with Pydantic library which is a dependency for openepd package. If you use Pydantic in your project
69
- carefully pick the version:
70
-
71
- * Use version **below** `2.0.0` if your project uses Pydantic version below `2.0.0`
72
- * Use version `2.x.x` or higher if your project uses Pydantic version `2.0.0` or above
73
-
74
84
  ### Models
75
85
 
76
86
  The library provides the Pydantic models for all the OpenEPD entities. The models are available in the `openepd.models`