openepd 6.31.2__py3-none-any.whl → 6.32.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.
openepd/__version__.py CHANGED
@@ -13,4 +13,4 @@
13
13
  # See the License for the specific language governing permissions and
14
14
  # limitations under the License.
15
15
  #
16
- VERSION = "6.31.2"
16
+ VERSION = "6.32.0"
openepd/model/common.py CHANGED
@@ -15,8 +15,9 @@
15
15
  #
16
16
  from collections.abc import Callable, Generator
17
17
  from enum import StrEnum
18
+ import math
18
19
  import re
19
- from typing import Annotated, Any
20
+ from typing import Annotated, Any, Final
20
21
 
21
22
  from openepd.compat.pydantic import pyd
22
23
  from openepd.model.base import BaseOpenEpdSchema
@@ -31,6 +32,14 @@ in web pages as if they were external resources.
31
32
  The pattern matches the following format: data:[<media-type>][;base64],<data>
32
33
  """
33
34
 
35
+ DATA_URL_IMAGE_MAX_LENGTH: Final[int] = math.ceil(32 * 1024 * 4 / 3)
36
+ """
37
+ Maximum allowed length of a data URL image string to ensure the decoded image is less than 32KB.
38
+
39
+ Base64 encoding overhead (approximately 33%) requires
40
+ limiting the encoded string length to 4/3 of the file size limit.
41
+ """
42
+
34
43
 
35
44
  class Amount(BaseOpenEpdSchema):
36
45
  """A value-and-unit pairing for amounts that do not have an uncertainty."""
@@ -336,3 +345,17 @@ class DataUrl(str):
336
345
  raise ValueError(msg)
337
346
 
338
347
  yield validator
348
+
349
+
350
+ def validate_data_url(v: str | None, max_length: int) -> None:
351
+ """
352
+ Validate max length of data URL.
353
+
354
+ :param v: data URL string.
355
+ :param max_length: maximum allowed length.
356
+ :raises ValueError: if the data URL exceeds the maximum length.
357
+ :return: None
358
+ """
359
+ if v and len(v) > max_length:
360
+ msg = f"URL must not exceed {max_length} characters"
361
+ raise ValueError(msg)
@@ -16,12 +16,10 @@
16
16
  import abc
17
17
  import datetime
18
18
  from enum import StrEnum
19
- import math
20
- from typing import Final
21
19
 
22
20
  from openepd.compat.pydantic import pyd
23
21
  from openepd.model.base import BaseOpenEpdSchema, OpenXpdUUID, RootDocument
24
- from openepd.model.common import Amount, DataUrl
22
+ from openepd.model.common import DATA_URL_IMAGE_MAX_LENGTH, Amount, DataUrl, validate_data_url
25
23
  from openepd.model.geography import Geography
26
24
  from openepd.model.org import Org
27
25
  from openepd.model.pcr import Pcr
@@ -34,14 +32,6 @@ DEVELOPER_DESCRIPTION = "The organization responsible for the underlying LCA (an
34
32
  PROGRAM_OPERATOR_DESCRIPTION = "JSON object for program operator Org"
35
33
  THIRD_PARTY_VERIFIER_DESCRIPTION = "JSON object for Org that performed a critical review of the EPD data"
36
34
 
37
- PRODUCT_IMAGE_MAX_LENGTH: Final[int] = math.ceil(32 * 1024 * 4 / 3)
38
- """
39
- Maximum length for product_image, product_image_small fields.
40
-
41
- Image file size must be less than 32KB. Base64 encoding overhead (approximately 33%) requires
42
- limiting the encoded string length to 4/3 of the file size limit.
43
- """
44
-
45
35
 
46
36
  class BaseDeclaration(RootDocument, abc.ABC):
47
37
  """Base class for declaration-related documents (EPDs, Industry-wide EPDs, Generic Estimates)."""
@@ -175,9 +165,7 @@ class BaseDeclaration(RootDocument, abc.ABC):
175
165
 
176
166
  @pyd.validator("product_image", "product_image_small")
177
167
  def validate_product_image(cls, v: str | None) -> str | None:
178
- if v and len(v) > PRODUCT_IMAGE_MAX_LENGTH:
179
- msg = f"URL must not exceed {PRODUCT_IMAGE_MAX_LENGTH} characters"
180
- raise ValueError(msg)
168
+ validate_data_url(v, DATA_URL_IMAGE_MAX_LENGTH)
181
169
  return v
182
170
 
183
171
 
openepd/model/epd.py CHANGED
@@ -16,7 +16,14 @@
16
16
 
17
17
  from openepd.compat.pydantic import pyd
18
18
  from openepd.model.base import BaseDocumentFactory, OpenEpdDoctypes, OpenEpdExtension
19
- from openepd.model.common import Ingredient, WithAltIdsMixin, WithAttachmentsMixin
19
+ from openepd.model.common import (
20
+ DATA_URL_IMAGE_MAX_LENGTH,
21
+ DataUrl,
22
+ Ingredient,
23
+ WithAltIdsMixin,
24
+ WithAttachmentsMixin,
25
+ validate_data_url,
26
+ )
20
27
  from openepd.model.declaration import (
21
28
  DEVELOPER_DESCRIPTION,
22
29
  PROGRAM_OPERATOR_DESCRIPTION,
@@ -200,14 +207,14 @@ class EpdPreviewV0(
200
207
  description="Text description of how product is typically used. Can be used to describe accessories "
201
208
  "like fasteners, adhesives, etc. Supports plain text or github flavored markdown.",
202
209
  )
203
- product_usage_image: pyd.AnyUrl | None = pyd.Field(
210
+ product_usage_image: pyd.AnyUrl | None | DataUrl = pyd.Field(
204
211
  description="Pointer (url) to image illustrating how the product is used. No more than 10MB.", default=None
205
212
  )
206
213
  manufacturing_description: str | None = pyd.Field(
207
214
  default=None,
208
215
  description="Text description of manufacturing process. Supports plain text or github flavored markdown.",
209
216
  )
210
- manufacturing_image: pyd.AnyUrl | None = pyd.Field(
217
+ manufacturing_image: pyd.AnyUrl | None | DataUrl = pyd.Field(
211
218
  description="Pointer (url) to an image illustrating the manufacturing process. No more than 10MB.", default=None
212
219
  )
213
220
 
@@ -235,6 +242,11 @@ class EpdPreviewV0(
235
242
  msg = "Invalid doctype"
236
243
  raise ValueError(msg)
237
244
 
245
+ @pyd.validator("product_usage_image", "manufacturing_image")
246
+ def validate_product_image(cls, v: str | None) -> str | None:
247
+ validate_data_url(v, DATA_URL_IMAGE_MAX_LENGTH)
248
+ return v
249
+
238
250
 
239
251
  EpdPreview = EpdPreviewV0
240
252
 
openepd/model/org.py CHANGED
@@ -13,24 +13,22 @@
13
13
  # See the License for the specific language governing permissions and
14
14
  # limitations under the License.
15
15
  #
16
- import math
17
- from typing import Annotated, Final, Optional
16
+ from typing import Annotated, Optional
18
17
 
19
18
  from openlocationcode import openlocationcode
20
19
 
21
20
  from openepd.compat.pydantic import pyd
22
21
  from openepd.model.base import BaseOpenEpdSchema
23
- from openepd.model.common import DataUrl, Location, WithAltIdsMixin, WithAttachmentsMixin
22
+ from openepd.model.common import (
23
+ DATA_URL_IMAGE_MAX_LENGTH,
24
+ DataUrl,
25
+ Location,
26
+ WithAltIdsMixin,
27
+ WithAttachmentsMixin,
28
+ validate_data_url,
29
+ )
24
30
  from openepd.model.validation.common import ReferenceStr
25
31
 
26
- ORG_LOGO_MAX_LENGTH: Final[int] = math.ceil(32 * 1024 * 4 / 3)
27
- """
28
- Maximum length of Org.logo field.
29
-
30
- Logo file size must be less than 32KB. Base64 encoding overhead (approximately 33%) requires
31
- limiting the encoded string length to 4/3 of the file size limit.
32
- """
33
-
34
32
 
35
33
  class OrgRef(BaseOpenEpdSchema):
36
34
  """Represents Organisation with minimal data."""
@@ -105,9 +103,7 @@ class Org(WithAttachmentsMixin, WithAltIdsMixin, OrgRef):
105
103
 
106
104
  @pyd.validator("logo")
107
105
  def validate_logo(cls, v: str | None) -> str | None:
108
- if v and len(v) > ORG_LOGO_MAX_LENGTH:
109
- msg = f"Logo URL must not exceed {ORG_LOGO_MAX_LENGTH} characters"
110
- raise ValueError(msg)
106
+ validate_data_url(v, DATA_URL_IMAGE_MAX_LENGTH)
111
107
  return v
112
108
 
113
109
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: openepd
3
- Version: 6.31.2
3
+ Version: 6.32.0
4
4
  Summary: Python library to work with OpenEPD format
5
5
  License: Apache-2.0
6
6
  Author: C-Change Labs
@@ -1,5 +1,5 @@
1
1
  openepd/__init__.py,sha256=fhxfEyEurLvSfvQci-vb3njzl_lvhcLXiZrecCOaMU8,794
2
- openepd/__version__.py,sha256=UFkCgstvKywEim-Fe7_uHEzuZwO946IbHObLbqlHfYQ,639
2
+ openepd/__version__.py,sha256=B29m_wJ1prKL-Kg5P_HiWaVxsRcfdLKlPzjM3pCaZ5w,639
3
3
  openepd/api/__init__.py,sha256=9THJcV3LT7JDBOMz1px-QFf_sdJ0LOqJ5dmA9Dvvtd4,620
4
4
  openepd/api/average_dataset/__init__.py,sha256=9THJcV3LT7JDBOMz1px-QFf_sdJ0LOqJ5dmA9Dvvtd4,620
5
5
  openepd/api/average_dataset/generic_estimate_sync_api.py,sha256=_eZt_jGVL1a3p9cr-EF39Ve9Vl5sB8zwzTc_slnRL50,7975
@@ -46,15 +46,15 @@ openepd/m49/utils.py,sha256=vQl0wMXtYS2b7NeLIWilDNUopq3MATmLnhEFcMYTeZA,7256
46
46
  openepd/model/__init__.py,sha256=9THJcV3LT7JDBOMz1px-QFf_sdJ0LOqJ5dmA9Dvvtd4,620
47
47
  openepd/model/base.py,sha256=WAnVnPdsvkRumsDV6XEnYYhFjexIO1ttmzB9znkA8r8,9929
48
48
  openepd/model/category.py,sha256=reeOVRDuZPYU77EMwG0K5VjnK2H9yOGxT0PJXXqrjEk,1639
49
- openepd/model/common.py,sha256=YIEvA_SfUIBC2LshxqPU8jFDp0mvaDT6jNgcJXgQnQk,13877
50
- openepd/model/declaration.py,sha256=F7LEMeZSJndtvO0AqnvTS43MeRxI3MO1IWLyF1SlwPU,14744
51
- openepd/model/epd.py,sha256=SGAgEEAuvRGsIlgIUI60UqV1alhb5kouOvp8fAC0VPg,12320
49
+ openepd/model/common.py,sha256=xJmu3PjvQwBHH8AUM247woOZeQ-r7cPI8qHBiN7Uorc,14601
50
+ openepd/model/declaration.py,sha256=6QyFsAxbuwRiVc6vGND-tQff1IGctPjaJj3ds-8TA4Y,14343
51
+ openepd/model/epd.py,sha256=WNxFZvcPhMPUfl-HbtDHaAP4QSm7_uG4juuZxOPJwYM,12629
52
52
  openepd/model/factory.py,sha256=UWSGpfCr3GiMTP4rzBkwqxzbXB6GKZ_5Okb1Dqa_4aA,2701
53
53
  openepd/model/generic_estimate.py,sha256=zLGTyf4Uzmp2C0m-J1ePWItSz2RGdZ0OiGPWC5nhKHk,3992
54
54
  openepd/model/geography.py,sha256=Jx7NIDdk_sIvwyh-7YxnIjAwIHW2HCQK7UtFGM2xKtw,42095
55
55
  openepd/model/industry_epd.py,sha256=QZr7OhgGkzqZ8H5p6dCIVk9zSHEYtK3y9Nk-DvkFMyk,4011
56
56
  openepd/model/lcia.py,sha256=PdaSZz02PMBJl_t2B_dmkcbzIFjA7iACrWj1fllHfw0,26230
57
- openepd/model/org.py,sha256=qP4VED4bcgwgyMARHF7S1LX7lkqCXMyxeIqfjgOI8gs,7927
57
+ openepd/model/org.py,sha256=XIBDIiu-s_GZ9op9X8Eo-BAwGMUC_ecajMERj7FtoM0,7612
58
58
  openepd/model/pcr.py,sha256=7nf6ATofdrlPt81vdU6p0E8n_ftFEuCEIKxtYlFwclw,5476
59
59
  openepd/model/specs/README.md,sha256=UGhSiFJ9hOxT1mZl-5ZrhkOrPKf1W_gcu5CI9hzV7LU,2430
60
60
  openepd/model/specs/__init__.py,sha256=YbyiLnQRQIX9mEdfQRVP3Rp1q3iNwujQMBbDK2-CYdM,3836
@@ -158,7 +158,7 @@ openepd/utils/__init__.py,sha256=9THJcV3LT7JDBOMz1px-QFf_sdJ0LOqJ5dmA9Dvvtd4,620
158
158
  openepd/utils/functional.py,sha256=sm7od2_UE-cNToezBlwFQ1TCUJub1tz6VykA1X8XH-I,1274
159
159
  openepd/utils/mapping/__init__.py,sha256=9THJcV3LT7JDBOMz1px-QFf_sdJ0LOqJ5dmA9Dvvtd4,620
160
160
  openepd/utils/mapping/common.py,sha256=WphCzwQQlzX11tUk88Ubyq3QPBLvH0tBPSIuH0kmiug,7339
161
- openepd-6.31.2.dist-info/LICENSE,sha256=xx0jnfkXJvxRnG63LTGOxlggYnIysveWIZ6H3PNdCrQ,11357
162
- openepd-6.31.2.dist-info/METADATA,sha256=VYAkuvgVPMCgqN6CP6nATMUCxPxnuf7zoQs7FaQ-bnw,9827
163
- openepd-6.31.2.dist-info/WHEEL,sha256=b4K_helf-jlQoXBBETfwnf4B04YC67LOev0jo4fX5m8,88
164
- openepd-6.31.2.dist-info/RECORD,,
161
+ openepd-6.32.0.dist-info/LICENSE,sha256=xx0jnfkXJvxRnG63LTGOxlggYnIysveWIZ6H3PNdCrQ,11357
162
+ openepd-6.32.0.dist-info/METADATA,sha256=eagaMXfquwAt_2AXDJyK2NyJsu_8eRgsj1lzulvk_Fg,9827
163
+ openepd-6.32.0.dist-info/WHEEL,sha256=b4K_helf-jlQoXBBETfwnf4B04YC67LOev0jo4fX5m8,88
164
+ openepd-6.32.0.dist-info/RECORD,,