Skip to content

blext.extyp

blext.extyp.bl_manifest

Implements strictly validated Blender manifest schemas.

ATTRIBUTE DESCRIPTION
_RE_MANIFEST_SEMVER

Lifted directly from the source code of Blender 4.2.0.

BLManifest

Bases: Protocol

Protocol defining behavior that all Blender manifests must exhibit.

manifest_filename property

manifest_filename: str

Name of the manifest file to write.

schema_version property

schema_version: str

SemVer version of the Blender manifest schema.

BLManifest_1_0_0

Bases: BaseModel

Strict representation of the 1.0.0 version of the Blender extension manifest.

Notes

Source of Validation Information: addons_core.bl_pkg.cli.blender_ext.

The validation routines performed on these fields should flawlessly match that done by blender --command extension validate, for Blender version 4.2.0.

The validation of this class is derived from the following 4.2.0 source-code permalinks (presuming struct=True):

Why 4.2.0? This was the first version to support the 1.0.0 manifest schema.


blext enforces strict adherance to the 1.0.0 manifest specification and/or 4.2.0 behavior to support ecosystem integrity: An extension with a manifest that parses correctly in one version of Blender, must parse correctly in all future versions of Blender that support this same schema. No exceptions.

Thus, any blext deviation in manifest parsing from the 4.2.0 behavior of blender --command extension validate, or from the original manifest 1.0.0 specification, should be considered (and reported) as a bug in blext. If they conflict, prefer the original manifest 1.0.0 specification.

Conversely, unless the original manifest 1.0.0 specification explicitly states otherwise, all Blender versions that support manifest 1.0.0 must do so with identical semantics / edge cases to 4.2.0. If a Blender version's 1.0.0 manifest parsing differs from the specification, or from 4.2.0's behavior, then this is a bug in that version of Blender, which should be reported upstream ASAP.

ATTRIBUTE DESCRIPTION
id

The short name of the extension.

TYPE: str


blext.extyp.bl_manifest_version

Defines the Blender extension specification.

BLManifestVersion

Bases: StrEnum

Known Blender extension manifest schema versions.

manifest_type cached property

manifest_type: type[BLManifest]

Class representing this Blender manifest schema.

semantic_version cached property

semantic_version: Version

Class representing this Blender manifest schema.


blext.extyp.bl_platform

Extension types serving as meaningful abstractions for managing Blender extensions.

ATTRIBUTE DESCRIPTION
ValidBLExtPerms

Hardcoded list of valid extension permissions. - files: Access to any filesystem operations. - network: Access to the internet. - clipboard: Permission to read and/or write the system clipboard. - camera: Permission to capture photos or videos from system sources. - microphone: Permission to capture audio from system sources.

ValidBLTags

Hardcoded list of valid extension tags.

BLPlatform

Bases: StrEnum

Identifier for a particular kind of OS/Architecture supported by Blender.

Notes

Values correspond perfectly to the platforms defined in the official Blender extension manifest specification.

However, note that there are many nuances and conventions when it comes to cross-platform identification of architectures. When interacting with other systems, ensure this is taken into account.

Corresponds perfectly to the platforms defined in the Blender Extension Manifest.

See Also
  • blext.finders: Tools for detecting BLPlatforms.

is_windows cached property

is_windows: bool

Whether this is a Windows-based platform.

official_archive_file_ext cached property

official_archive_file_ext: str

File extension of Blender distributed officially and portably for this platform.

pymarker_os_name cached property

pymarker_os_name: Literal['posix', 'nt']

Value of os.name on the given Blender platform.

Notes

Does not consider PEP600 references.

See Also
  • PEP600: https://peps.python.org/pep-0600/

pymarker_platform_machines cached property

pymarker_platform_machines: frozenset[str]

Value of os.name on the given Blender platform.

Notes

Does not consider PEP600 references.

See Also
  • PEP600: https://peps.python.org/pep-0600/

pymarker_platform_system cached property

pymarker_platform_system: Literal[
	'Linux', 'Darwin', 'Windows'
]

Value of platform.system() on the given Blender platform.

Notes

Does not consider PEP600 references.

See Also
  • PEP600: https://peps.python.org/pep-0600/

pymarker_sys_platform cached property

pymarker_sys_platform: Literal['linux', 'darwin', 'win32']

Value of os.name on the given Blender platform.

Notes

Does not consider PEP600 references.

See Also
  • PEP600: https://peps.python.org/pep-0600/

pypi_arches cached property

pypi_arches: frozenset[str]

Set of PyPi CPU-architecture tags supported by this BLPlatform.

Notes

PyPi is the official platform for distributing Python dependencies as ex. wheels. For example, it is the default source for pip install *.

PyPi has its own conventions for tagging CPU architectures, including the universal* tags for MacOS. Therefore, a bridge must be built, by asking the following question:

    - Each `BLPlatform` **implicitly** supports a number of CPU architectures.
    - Each Python dependency wheel **implicitly** supports a number of CPU architectures.
    - _What's the overlap?_

This property answers that question using a hard-coded mapping from each BLPlatform, to the set of all PyPi CPU architecture tags that should be considered identical.

wheel_platform_tag_prefix cached property

wheel_platform_tag_prefix: str

Prefix of compatible wheel platform tags.

Notes

Does not consider PEP600 references.

See Also
  • PEP600: https://peps.python.org/pep-0600/

blext.extyp.bl_release_discovered

Implements BLReleaseDiscovered.

BLReleaseDiscovered

Bases: BaseModel

Identifier for a supported version of Blender.

References

bl_version cached property

bl_version: BLVersion

The Blender version corresponding to this release.

from_blender_version_output classmethod

from_blender_version_output(
	blender_version_output: str,
) -> Self

Parse the output of blender --version to create this object.

Source code in blext/extyp/bl_release_discovered.py
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
@classmethod
def from_blender_version_output(cls, blender_version_output: str) -> typ.Self:  # noqa: C901
	"""Parse the output of `blender --version` to create this object."""
	lines = [
		line.strip()
		for line in blender_version_output.strip().split('\n')
		if line.strip()
	]

	####################
	# - Stage 0: Parse Fields
	####################
	if lines[0].startswith('Blender'):
		official_version_str = lines[0].split(' ')[1]
		official_version = tuple(int(el) for el in official_version_str.split('.'))
		if len(official_version) != 3:  # noqa: PLR2004
			msg = [
				"First line of `blender --version` string doesn't contain an official `M.m.p` version of Blender.",
				f'> First line of `blender --version`: {lines[0]}',
			]
			raise ValueError(msg)
	else:
		msgs = [
			"First line of `blender --version` string doesn't start with `Blender`.",
			f'> First line of `blender --version`: {lines[0]}',
		]
		raise ValueError(*msgs)

	parsed: dict[str, typ.Any] = {}
	for line in filter(lambda line: ':' in line, lines[1:]):
		line_split = line.split(':')
		match line_split[0]:
			case 'build date' | 'build commit date':
				parsed[line_split[0].replace(' ', '_')] = dtime.date.fromisoformat(
					line_split[1].strip()
				)

			case 'build time' | 'build commit time':
				parsed[line_split[0].replace(' ', '_')] = dtime.time.fromisoformat(
					line_split[1].strip()
				)

			case (
				'build hash'
				| 'build branch'
				| 'build platform'
				| 'build type'
				| 'build system'
			):
				parsed[line_split[0].replace(' ', '_')] = line_split[1].strip()

			case 'build c flags' | 'build c++ flags' | 'build link flags':
				parsed[line_split[0].replace(' ', '_').replace('++', 'pp')] = tuple(
					flag.strip()
					for flag in line_split[1].strip().split(' ')
					if flag.strip()
				)

			case _:
				msgs = [
					'Tried to parse unknown line from `blender --version`.',
					f'> Invalid line of `blender --version`: {line}',
				]
				raise ValueError(*msgs)

	missing_keys: list[str] = []
	for key in [
		'build_date',
		'build_time',
		'build_commit_date',
		'build_commit_time',
		'build_hash',
		'build_branch',
		'build_platform',
		'build_type',
		'build_c_flags',
		'build_cpp_flags',
		'build_link_flags',
		'build_system',
	]:
		if key not in parsed:
			missing_keys.append(key)

	if not missing_keys:
		return cls(
			official_version=official_version,
			build_datetime=dtime.datetime.combine(
				date=parsed['build_date'],  # pyright: ignore[reportAny]
				time=parsed['build_time'],  # pyright: ignore[reportAny]
			),
			build_commit_datetime=dtime.datetime.combine(
				date=parsed['build_commit_date'],  # pyright: ignore[reportAny]
				time=parsed['build_commit_time'],  # pyright: ignore[reportAny]
			),
			build_hash=parsed['build_hash'],  # pyright: ignore[reportAny]
			build_branch=parsed['build_branch'],  # pyright: ignore[reportAny]
			build_platform=parsed['build_platform'],  # pyright: ignore[reportAny]
			build_type=parsed['build_type'],  # pyright: ignore[reportAny]
			build_c_flags=parsed['build_c_flags'],  # pyright: ignore[reportAny]
			build_cpp_flags=parsed['build_cpp_flags'],  # pyright: ignore[reportAny]
			build_link_flags=parsed['build_link_flags'],  # pyright: ignore[reportAny]
			build_system=parsed['build_system'],  # pyright: ignore[reportAny]
		)

	msgs = [
		f'`blender --version` did not contain {len(missing_keys)} expected keys:',
		*[f' - {missing_key}' for missing_key in missing_keys],
	]
	raise ValueError(*msgs)

blext.extyp.bl_release_official

Implements BLReleaseOfficial.

BLReleaseOfficial

Bases: StrEnum

Identifier for a supported version of Blender.

References

bl_version cached property

bl_version: BLVersion

The Blender version corresponding to this release.

is_4_2 cached property

is_4_2: bool

Whether this version of Blender is a 4.2.* release.

is_4_3 cached property

is_4_3: bool

Whether this version of Blender is a 4.3.* release.

is_4_4 cached property

is_4_4: bool

Whether this version of Blender is a 4.4.* release.

is_4_5 cached property

is_4_5: bool

Whether this version of Blender is a 4.5.* release.

is_5_0 cached property

is_5_0: bool

Whether this version of Blender is a 5.0.* release.

min_glibc_version cached property

min_glibc_version: tuple[int, int]

Minimum glibc version suported on Linux variants of this Blender version.

min_macos_version cached property

min_macos_version: tuple[int, int]

Minimum macos version suported on MacOS variants of this Blender version.

official_git_tag cached property

official_git_tag: str

Name of git tag corresponding to this Blender version.

Notes

For all self.supported_bl_platforms, this tag is presumed to be valid also for the submodule repositories in lib/lib-<bl_platform.

official_version cached property

official_version: tuple[int, int, int]

The official version tuple associated with this release.

py_sys_version cached property

py_sys_version: tuple[int, int, int, str, int]

Value of sys.implementation.version in this Blender version.

Notes
  • On CPython, sys.version_info is the same as sys.implementation.version.
  • The exact (including patch) version of Python shipped with each Blender version is given by-platform, in the submodules of lib/<bl_platform>.
  • The exact Python version in use within each platform can be read from python/include/python3.11/patchlevel.h.

In patchlevel.h, a block such as the following can be found:

/* Version parsed out into numeric values */
/*--start constants--*/
#define PY_MAJOR_VERSION        3
#define PY_MINOR_VERSION        11
#define PY_MICRO_VERSION        11
#define PY_RELEASE_LEVEL        PY_RELEASE_LEVEL_FINAL
#define PY_RELEASE_SERIAL       0

Since Blender only uses CPython, this corresponds to both sys.version_info and sys.implementation.version.

Tips: The M.m.* versions often have matching Python versions across platforms and patch-versions. This can be manually validated, one by one, by inserting every supported <bl_platform> and <git_tag> tag into the following URL:

https://projects.blender.org/blender/lib-<bl_platform>/src/tag/<git_tag>/python/include/python3.11/patchlevel.h

Python Version: It's presumed that the exact Python version, including patch-versions, is identical across all platforms.

pymarker_extra cached property

pymarker_extra: Literal[
	'blender4-2', 'blender4-3', 'blender4-4'
]

Optional dependency extra name corresponding to this Blender version.

Notes

This is a key to the [project.optional-dependencies] table in pyproject.toml, which defines Blender version-specific dependencies.

Blender's bundled site-packages must be matched as much as possible, under this property's key, to ensure that uv's resolver finds compatible dependency versions.

Compared to the keys of [project.optional-dependencies] in pyproject.toml, this property uses - instead of _.

This matches how the extra is defined in uv.lock.

pymarker_implementation_name cached property

pymarker_implementation_name: Literal['cpython']

Value of sys.implementation.name in this Blender version.

pymarker_platform_python_implementation cached property

pymarker_platform_python_implementation: Literal['CPython']

Value of sys.implementation.name in this Blender version.

python_version cached property

python_version: str

major.minor version of Python shipped with this Blender version.

valid_abi_tags cached property

valid_abi_tags: frozenset[str]

Tags on Python wheels that indicate ABI compatibility with this Blender version's Python environment.

valid_bl_platforms cached property

valid_bl_platforms: frozenset[BLPlatform]

Officially supported BLPlatforms of this Blender version.

Notes

Only platforms with a lib/ submodule containing tagged Blender versions are included. This may not correspond to the available official binary downloads.

It may be possible to compile Blender manually for wider platform support, but this isn't taken in to account by blext.

valid_extension_tags cached property

valid_extension_tags: frozenset[str]

Extension tags parseable by this Blender release.

valid_manifest_versions cached property

valid_manifest_versions: frozenset[BLManifestVersion]

Manifest versions supported by this Blender release.

valid_python_tags cached property

valid_python_tags: frozenset[str]

Tags on Python wheels that indicate interpreter compatibility with this Blender version's Python environment.

vendored_site_packages cached property

vendored_site_packages: frozendict[str, str]

Extension tags parseable by this Blender release.

from_official_version_range classmethod

from_official_version_range(
	bl_version_min_str: str,
	bl_version_max_str: str | None = None,
) -> frozenset[Self]

All ReleasedBLVersions within an inclusive/exclusive version range.

Source code in blext/extyp/bl_release_official.py
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
@classmethod
def from_official_version_range(
	cls, bl_version_min_str: str, bl_version_max_str: str | None = None, /
) -> frozenset[typ.Self]:
	"""All `ReleasedBLVersion`s within an inclusive/exclusive version range."""
	bl_version_min = tuple(int(el) for el in bl_version_min_str.split('.'))
	bl_version_max = (
		tuple(int(el) for el in bl_version_max_str.split('.'))
		if bl_version_max_str is not None
		else None
	)
	return frozenset(  # pyright: ignore[reportReturnType]
		{
			released_bl_version
			for released_bl_version in BLReleaseOfficial
			if (
				(
					released_bl_version.official_version[0] == bl_version_min[0]
					and released_bl_version.official_version[1] == bl_version_min[1]
					and released_bl_version.official_version[2] >= bl_version_min[2]
				)
				or (
					released_bl_version.official_version[0] == bl_version_min[0]
					and released_bl_version.official_version[1] > bl_version_min[1]
				)
				or released_bl_version.official_version[0] > bl_version_min[0]
			)
			and (
				bl_version_max is None
				or (
					released_bl_version.official_version[0] == bl_version_max[0]
					and released_bl_version.official_version[1] == bl_version_max[1]
					and released_bl_version.official_version[2] < bl_version_max[2]
				)
				or (
					released_bl_version.official_version[0] == bl_version_max[0]
					and released_bl_version.official_version[1] < bl_version_max[1]
				)
				or released_bl_version.official_version[0] < bl_version_max[0]
			)
		}
	)

released_4_2 classmethod

released_4_2() -> frozenset[Self]

All released 4.2 LTS versions of Blender.

Source code in blext/extyp/bl_release_official.py
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
@classmethod
def released_4_2(cls) -> frozenset[typ.Self]:
	"""All released `4.2 LTS` versions of Blender."""
	V = BLReleaseOfficial
	return frozenset(  # pyright: ignore[reportReturnType]
		{
			V.BL4_2_0,
			V.BL4_2_1,
			V.BL4_2_2,
			V.BL4_2_3,
			V.BL4_2_4,
			V.BL4_2_5,
			V.BL4_2_6,
			V.BL4_2_7,
			V.BL4_2_8,
		}
	)

released_4_3 classmethod

released_4_3() -> frozenset[Self]

All released 4.3 versions of Blender.

Source code in blext/extyp/bl_release_official.py
78
79
80
81
82
83
84
85
86
87
88
@classmethod
def released_4_3(cls) -> frozenset[typ.Self]:
	"""All released `4.3` versions of Blender."""
	V = BLReleaseOfficial
	return frozenset(  # pyright: ignore[reportReturnType]
		{
			V.BL4_3_0,
			V.BL4_3_1,
			V.BL4_3_2,
		}
	)

released_4_4 classmethod

released_4_4() -> frozenset[Self]

All released 4.4 versions of Blender.

Source code in blext/extyp/bl_release_official.py
90
91
92
93
94
@classmethod
def released_4_4(cls) -> frozenset[typ.Self]:
	"""All released `4.4` versions of Blender."""
	V = BLReleaseOfficial
	return frozenset({V.BL4_4_0})  # pyright: ignore[reportReturnType]

released_4_5 classmethod

released_4_5() -> frozenset[Self]

All released 4.5 LTS versions of Blender.

Source code in blext/extyp/bl_release_official.py
96
97
98
99
@classmethod
def released_4_5(cls) -> frozenset[typ.Self]:
	"""All released `4.5 LTS` versions of Blender."""
	return frozenset()

released_5_0 classmethod

released_5_0() -> frozenset[Self]

All released 5.0 versions of Blender.

Source code in blext/extyp/bl_release_official.py
101
102
103
104
@classmethod
def released_5_0(cls) -> frozenset[typ.Self]:
	"""All released `5.0` versions of Blender."""
	return frozenset()

blext.extyp.bl_version

Extension types serving as meaningful abstractions for managing Blender extensions.

ATTRIBUTE DESCRIPTION
ValidBLExtPerms

Hardcoded list of valid extension permissions. - files: Access to any filesystem operations. - network: Access to the internet. - clipboard: Permission to read and/or write the system clipboard. - camera: Permission to capture photos or videos from system sources. - microphone: Permission to capture audio from system sources.

ValidBLTags

Hardcoded list of valid extension tags.

BLVersion

Bases: BaseModel

Identifier for a supported version of Blender.

References

max_manifest_version cached property

max_manifest_version: BLManifestVersion

The latest supported Blender manifest version.

pymarker_implementation_version cached property

pymarker_implementation_version: str

Value of sys.implementation.version in this Blender version.

Reference

version cached property

version: str

This Blender version as a string.

Notes

Do not parse in hopes of learning anything about where this Blender version came from.

Very little can be presumed about this string. It could be a nice version tuple, a raw SHA256 commit ID, a special buildbot identifier, or something totally different.

is_smooshable_with

is_smooshable_with(
	other: Self,
	*,
	ext_bl_platforms: frozenset[BLPlatform] | None = None,
	ext_wheels_python_tags: frozenset[str] | None = None,
	ext_wheels_abi_tags: frozenset[str] | None = None,
	ext_tags: frozenset[str] | None = None,
) -> bool

Will an extension that works with one version, also work with the other?

PARAMETER DESCRIPTION
other

The candidate for smooshing with self.

TYPE: Self

ext_bl_platforms

Blender platforms supported by the extension. - When given, valid_bl_platforms on self and other must merely be non-strict supersets of this.

TYPE: frozenset[BLPlatform] | None DEFAULT: None

ext_wheels_python_tags

All valid Python tags given by all extension wheels. - When given, valid_python_tags on self and other must merely be non-strict supersets of this. - Extensions without wheels will have this as {}, causing this check to always return True.

TYPE: frozenset[str] | None DEFAULT: None

ext_wheels_abi_tags

All valid ABI tags given by all extension wheels. - When given, valid_abi_tags on self and other must merely be non-strict supersets of this. - Extensions without wheels will have this as none, causing this check to always return True (since all BLVersions must support none).

TYPE: frozenset[str] | None DEFAULT: None

ext_tags

All extension tags declared by the extension. - When given, valid_extension_tags on self and other must merely be non-strict supersets of this.

TYPE: frozenset[str] | None DEFAULT: None

Source code in blext/extyp/bl_version.py
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
def is_smooshable_with(
	self,
	other: typ.Self,
	*,
	ext_bl_platforms: frozenset[BLPlatform] | None = None,
	ext_wheels_python_tags: frozenset[str] | None = None,
	ext_wheels_abi_tags: frozenset[str] | None = None,
	ext_tags: frozenset[str] | None = None,
) -> bool:
	"""Will an extension that works with one version, also work with the other?

	Parameters:
		other: The candidate for smooshing with `self`.
		ext_bl_platforms: Blender platforms supported by the extension.
			- When given, `valid_bl_platforms` on `self` and `other` must merely be non-strict supersets of this.
		ext_wheels_python_tags: All valid Python tags given by all extension wheels.
			- When given, `valid_python_tags` on `self` and `other` must merely be non-strict supersets of this.
			- Extensions without wheels will have this as `{}`, causing this check to always return `True`.
		ext_wheels_abi_tags: All valid ABI tags given by all extension wheels.
			- When given, `valid_abi_tags` on `self` and `other` must merely be non-strict supersets of this.
			- Extensions without wheels will have this as `none`, causing this check to always return `True` (since all `BLVersion`s must support `none`).
		ext_tags: All extension tags declared by the extension.
			- When given, `valid_extension_tags` on `self` and `other` must merely be non-strict supersets of this.
	"""
	return (
		# Must have at least one blender_manifest.toml schema version in common.
		## - It'd be quite impossible to make "one extension for both" without this.
		len(self.valid_manifest_versions & other.valid_manifest_versions) >= 1
		# Valid BLPlatforms must match.
		## - May be ignored if all *extension*-supported BLPlatforms are supported by both.
		## - NOTE: Each extension BLPlatform must work on at least one BLVersion.
		and (
			self.valid_bl_platforms == other.valid_bl_platforms
			if ext_bl_platforms is None
			else (
				ext_bl_platforms.issubset(self.valid_bl_platforms)
				and ext_bl_platforms.issubset(other.valid_bl_platforms)
			)
		)
		# Python tags (aka. Python versions) must match.
		## - Guarantees that any Python code that works on one, works on the other.
		and (
			self.valid_python_tags == other.valid_python_tags
			if ext_wheels_python_tags is None
			else (
				ext_wheels_python_tags.issubset(self.valid_python_tags)
				and ext_wheels_python_tags.issubset(other.valid_python_tags)
			)
		)
		# ABI tags must match.
		## - Guarantees that any wheel that works on one, works on the other.
		## - May be ignored if all extension wheel ABI tags are supported by both.
		and (
			self.valid_abi_tags == other.valid_abi_tags
			if ext_wheels_abi_tags is None
			else (
				ext_wheels_abi_tags.issubset(self.valid_abi_tags)
				and ext_wheels_abi_tags.issubset(other.valid_abi_tags)
			)
		)
		# Available / valid extension tags must match.
		## - Guarantees that any extension (info) tag parseable by one Blender, parses on the other.
		## - May be ignored if all extension (info) tags are supported by both.
		and (
			self.valid_extension_tags == other.valid_extension_tags
			if ext_tags is None
			else (
				ext_tags.issubset(self.valid_extension_tags)
				and ext_tags.issubset(other.valid_extension_tags)
			)
		)
	)

pymarker_encoded_package_extras

pymarker_encoded_package_extras(
	pkg_name: str,
) -> frozenset[str]

Encode the name of a pymarker extra, corresponding to a given package name.

Notes

Compared to the keys of [project.optional-dependencies] in pyproject.toml, this property uses - instead of _. This includes a transformation of pkg_name from use of _, to use of -.

This matches how the extra is defined in uv.lock.

blext uses different [project.optional-dependencies] to encode packages whose versions differ across versions of Blender / are directly vendored by Blender. As a result, uv automatically guarantees that any other dependencies one may add are compatible with all of the Blender versions supported by the extension.

However, by default, [project.optional-dependencies] are not allowed to conflict. To work around this, one can declare the following to uv:

[tool.uv]
conflicts = [
        [
                { extra = "bl4_2" },
                { extra = "bl4_3" },
                { extra = "bl4_4" },
        ],
]

However, in scipy's uv.lock markers, one may now see something like

[[package]]
name = "scipy"
version = "1.15.1"
source = { registry = "https://pypi.org/simple" }
dependencies = [
        { name = "numpy", version = "1.24.3", source = { registry = "https://pypi.org/simple" }, marker = "extra == 'extra-11-simple-proj-bl4-2' or extra == 'extra-11-simple-proj-bl4-3'" },
        { name = "numpy", version = "1.26.4", source = { registry = "https://pypi.org/simple" }, marker = "extra == 'extra-11-simple-proj-bl4-4' or (extra == 'extra-11-simple-proj-bl4-2' and extra == 'extra-11-simple-proj-bl4-3') or (extra != 'extra-11-simple-proj-bl4-2' and extra != 'extra-11-simple-proj-bl4-3')" },
]

When a non-optional package (ex. scipy) depends on a package defined only in one of the mutually exclusive optional dependencies (ex. numpy), uv seems to generate new extra names like: - extra-11-simple-proj-bl4-3 - extra-11-simple-proj-bl4-4 - extra-{len(pkg_name)}-{pkg_name}-{pymarker_extra}

Presumably, this prevents name-conflicts.

References
Source code in blext/extyp/bl_version.py
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
def pymarker_encoded_package_extras(self, pkg_name: str) -> frozenset[str]:
	"""Encode the name of a pymarker `extra`, corresponding to a given package name.

	Notes:
		Compared to the keys of `[project.optional-dependencies]` in `pyproject.toml`, this property uses `-` instead of `_`.
		This includes a transformation of `pkg_name` from use of `_`, to use of `-`.

		This matches how the `extra` is defined in `uv.lock`.

		`blext` uses different `[project.optional-dependencies]` to encode packages whose versions differ across versions of Blender / are directly vendored by Blender.
		As a result, `uv` automatically guarantees that any other dependencies one may add are compatible with all of the Blender versions supported by the extension.

		However, by default, `[project.optional-dependencies]` are not allowed to conflict.
		To work around this, one can declare the following to `uv`:

		```toml
		[tool.uv]
		conflicts = [
			[
				{ extra = "bl4_2" },
				{ extra = "bl4_3" },
				{ extra = "bl4_4" },
			],
		]
		```

		However, in `scipy`'s `uv.lock` markers, one may now see something like
		```toml
		[[package]]
		name = "scipy"
		version = "1.15.1"
		source = { registry = "https://pypi.org/simple" }
		dependencies = [
			{ name = "numpy", version = "1.24.3", source = { registry = "https://pypi.org/simple" }, marker = "extra == 'extra-11-simple-proj-bl4-2' or extra == 'extra-11-simple-proj-bl4-3'" },
			{ name = "numpy", version = "1.26.4", source = { registry = "https://pypi.org/simple" }, marker = "extra == 'extra-11-simple-proj-bl4-4' or (extra == 'extra-11-simple-proj-bl4-2' and extra == 'extra-11-simple-proj-bl4-3') or (extra != 'extra-11-simple-proj-bl4-2' and extra != 'extra-11-simple-proj-bl4-3')" },
		]
		```

		When a non-optional package (ex. `scipy`) depends on a package defined _only_ in one of the mutually exclusive optional dependencies (ex. `numpy`), `uv` seems to generate new `extra` names like:
		- `extra-11-simple-proj-bl4-3`
		- `extra-11-simple-proj-bl4-4`
		- `extra-{len(pkg_name)}-{pkg_name}-{pymarker_extra}`

		Presumably, this prevents name-conflicts.

	References:
		- `uv`'s `encode_package_extra()`: <https://github.com/astral-sh/uv/blob/3d946027841ac949b9ecfd5ceaeec721836ee555/crates/uv-resolver/src/universal_marker.rs#L499>
		- `packaging.markers` Environment Evaluation: <https://github.com/pypa/packaging/blob/main/src/packaging/markers.py#L204>
	"""
	pkg_name = pkg_name.replace('_', '-')
	return frozenset(
		{
			f'extra-{len(pkg_name)}-{pkg_name}-{pymarker_extra}'
			for pymarker_extra in self.pymarker_extras
		}
	)

pymarker_environments

pymarker_environments(
	*, pkg_name: str | None = None
) -> frozendict[
	BLPlatform, tuple[frozendict[str, str], ...]
]

All supported marker environments, indexed by BLPlatform.

Notes

Wheels that utilize platform_release marker dependencies are incompatible with Blender extensions.

This is because there's no way to predict the user's exact platform_release when preparing extension wheels.

Each element contains the following:

  • implementation_name: Always 'cpython', as Blender only supports the "CPython" Python interpreter. - Source: Clone of from sys.implementation.name.
  • implementation_version: Derived from each valid Blender version. - Source: Derived from the target's sys.implementation.version. See the official environment markers reference for an example of code that does this.
  • os_name: Derived from each valid extyp.BLPlatform.
  • platform_machine: Derived from each valid extyp.BLPlatform.
  • platform_release: Always empty-string, denoting "undefined". - Source: There's no way to know this in advance for vendored dependencies. It's wildly specific.
  • platform_python_implementation: Always 'CPython', as Blender only supports the "CPython" Python interpreter.
  • sys_platform: Derived from each valid BLVersion. - Source: Derived from target's sys.platform: https://docs.python.org/3/library/sys.html#sys.platform
See Also
Source code in blext/extyp/bl_version.py
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
def pymarker_environments(
	self,
	*,
	pkg_name: str | None = None,
) -> frozendict[BLPlatform, tuple[frozendict[str, str], ...]]:
	"""All supported marker environments, indexed by `BLPlatform`.

	Notes:
		**Wheels that utilize `platform_release` marker dependencies are incompatible with Blender extensions**.

		This is because there's no way to predict the user's exact `platform_release` when preparing extension wheels.

		Each element contains the following:

		- `implementation_name`: Always `'cpython'`, as Blender only supports the "CPython" Python interpreter.
			- **Source**: Clone of from `sys.implementation.name`.
		- `implementation_version`: Derived from each valid Blender version.
			- **Source**: Derived from the target's `sys.implementation.version`. See the [official environment markers reference](https://peps.python.org/pep-0508/#environment-markers) for an example of code that does this.
		- `os_name`: Derived from each valid `extyp.BLPlatform`.
		- `platform_machine`: Derived from each valid `extyp.BLPlatform`.
		- `platform_release`: Always empty-string, denoting "undefined".
			- **Source**: There's no way to know this in advance for vendored dependencies. It's wildly specific.
		- `platform_python_implementation`: Always `'CPython'`, as Blender only supports the "CPython" Python interpreter.
		- `sys_platform`: Derived from each valid `BLVersion`.
			- **Source**: Derived from target's `sys.platform`: <https://docs.python.org/3/library/sys.html#sys.platform>

	See Also:
		- Official Environment Marker Grammer: https://packaging.python.org/en/latest/specifications/dependency-specifiers/#environment-markers
		- Reference for `packaging.markers.Environment`: <https://packaging.pypa.io/en/stable/markers.html#packaging.markers.Environment>
	"""
	major, minor, patch, *_ = self.py_sys_version
	pymarker_platform_machines: dict[BLPlatform, list[str]] = {
		bl_platform: list(bl_platform.pymarker_platform_machines)
		for bl_platform in self.valid_bl_platforms
	}

	_initial_frozenset: frozenset[str] = frozenset()
	pymarker_environments = {
		bl_platform: [
			{
				'implementation_name': self.pymarker_implementation_name,
				'implementation_version': self.pymarker_implementation_version,
				'os_name': bl_platform.pymarker_os_name,
				'platform_machine': platform_machine,
				'platform_release': '',
				'platform_system': bl_platform.pymarker_platform_system,
				'python_full_version': f'{major}.{minor}.{patch}',
				'platform_python_implementation': self.pymarker_platform_python_implementation,
				'python_version': f'{major}.{minor}',
				'sys_platform': bl_platform.pymarker_sys_platform,
				'extra': pymarker_extra,
			}
			for platform_machine in pymarker_platform_machines[bl_platform]
			for pymarker_extra in (
				self.pymarker_extras
				| (
					_initial_frozenset
					if pkg_name is None
					else self.pymarker_encoded_package_extras(pkg_name)
				)
			)
		]
		for bl_platform in self.valid_bl_platforms
	}
	return deepfreeze(pymarker_environments)  # pyright: ignore[reportAny]

smoosh_with

smoosh_with(
	other: Self,
	ext_bl_platforms: frozenset[BLPlatform] | None = None,
	ext_wheels_python_tags: frozenset[str] | None = None,
	ext_wheels_abi_tags: frozenset[str] | None = None,
	ext_tags: frozenset[str] | None = None,
) -> Self

Chunk with another BLVersion, forming a new BLVersion encapsulating both.

Source code in blext/extyp/bl_version.py
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
def smoosh_with(
	self,
	other: typ.Self,
	ext_bl_platforms: frozenset[BLPlatform] | None = None,
	ext_wheels_python_tags: frozenset[str] | None = None,
	ext_wheels_abi_tags: frozenset[str] | None = None,
	ext_tags: frozenset[str] | None = None,
) -> typ.Self:
	"""Chunk with another `BLVersion`, forming a new `BLVersion` encapsulating both."""
	if self.is_smooshable_with(
		other,
		ext_bl_platforms=ext_bl_platforms,
		ext_wheels_python_tags=ext_wheels_python_tags,
		ext_wheels_abi_tags=ext_wheels_abi_tags,
		ext_tags=ext_tags,
	):
		return BLVersion(  # pyright: ignore[reportReturnType]
			source=BLVersionSourceSmooshed(
				# Every smoosh increments the "smoosh list", with order-preservation.
				sources=(
					(
						self.source.sources
						if isinstance(self.source, BLVersionSourceSmooshed)
						else frozenset({self.source})
					)
					| (
						other.source.sources
						if isinstance(other.source, BLVersionSourceSmooshed)
						else frozenset({other.source})
					)
				)
			),
			valid_manifest_versions=(
				self.valid_manifest_versions & other.valid_manifest_versions
			),
			valid_extension_tags=(
				self.valid_extension_tags & other.valid_extension_tags
			),
			valid_bl_platforms=(self.valid_bl_platforms & other.valid_bl_platforms),
			min_glibc_version=self.min_glibc_version,  ## TODO: Choose smallest.
			min_macos_version=self.min_macos_version,  ## TODO: Choose smallest.
			py_sys_version=self.py_sys_version,  ## TODO: Choose "smallest".
			valid_python_tags=(self.valid_python_tags & other.valid_python_tags),
			valid_abi_tags=(self.valid_abi_tags & other.valid_abi_tags),
			pymarker_extras=self.pymarker_extras | other.pymarker_extras,
			pymarker_implementation_name=self.pymarker_implementation_name,
			pymarker_platform_python_implementation=self.pymarker_platform_python_implementation,
			vendored_site_packages=self.vendored_site_packages,
		)

	msg = "Tried to 'smoosh' two incompatible BLVersions together."
	raise ValueError(msg)

blext.extyp.bl_version_source

Implements BLVersionSource.

BLVersionSource

Bases: BaseModel

A locatable version of Blender.

blender_version_max cached property

blender_version_max: str | None

Maximum supported Blender version, not including this version.

Notes

Do not parse: Only for specialized use.

Do not use self.version either.

Instead, handle versions in a manner consistent with each particular BLVersionSource subclass.

Other important properties: - Must correspond to an official release >=4.2.1. - Corresponds directly to the same-named property in blender_manifest.toml. - When not given, "all versions" are allowed.

blender_version_min cached property

blender_version_min: str

Minimum supported Blender version.

Notes

Do not parse: Only for specialized use.

Do not use self.version either.

Instead, handle versions in a manner consistent with each particular BLVersionSource subclass.

Other important properties: - Must correspond to an official releases >=4.2.0. - Corresponds directly to the same-named property in blender_manifest.toml. - The blender_manifest.toml format enforces that this be given by all BLVersions. For ex. forks that don't always have such a clean descriptor, it's okay to lie to make it work.

version cached property

version: str

Some kind of unique identifier of the referenced Blender version.


blext.extyp.bl_version_source_git

Implements BLVersionSourceOfficial.

BLVersionSourceGit

Bases: BLVersionSource

A version of Blender located at a git repository.

blender_version_min cached property

blender_version_min: str

Not yet implemented.

version cached property

version: str

Tag or commit ID of this Blender version.


blext.extyp.bl_version_source_official

Implements BLVersionSourceOfficial.

BLVersionSourceOfficial

Bases: BLVersionSource

An officially released version of Blender.

blender_version_max cached property

blender_version_max: str

The exact next M.m+1.0 version is the maximum.

blender_version_min cached property

blender_version_min: str

This exact M.m.p version is the minimum.

version cached property

version: str

Release version prefixed with v.

version_major_minor cached property

version_major_minor: str

Release version with no prefix, including only M.m.

version_major_minor_patch cached property

version_major_minor_patch: str

Release version with no prefix, including M.m.p.

portable_download_url

portable_download_url(bl_platform: BLPlatform) -> HttpUrl

URL to a portable variant of this Blender release.

Notes

Availability: Currently, it is not checked whether bl_platform has an official Blender download available.

Always check that this URL exists and looks reasonable before downloading anything.

Source code in blext/extyp/bl_version_source_official.py
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
def portable_download_url(self, bl_platform: BLPlatform) -> pyd.HttpUrl:
	"""URL to a portable variant of this Blender release.

	Notes:
		Availability: Currently, it is not checked whether `bl_platform` has an official Blender download available.

		Always check that this URL exists and looks reasonable before downloading anything.
	"""
	return pyd.HttpUrl(
		'/'.join(
			[
				str(self.base_download_url),
				f'Blender{self.version_major_minor}',
				f'blender-{self.version_major_minor}-{bl_platform}.{bl_platform.official_archive_file_ext}',
			]
		)
	)

blext.extyp.bl_version_source_smooshed

Implements BLVersionSource.

BLVersionSourceSmooshed

Bases: BLVersionSource

Several combined Blender version sources.

are_official_sources_consecutive cached property

are_official_sources_consecutive: bool

Whether all official sources are consecutive, without holes.

blender_version_max cached property

blender_version_max: str

The exact next M.m.p+1 version is the maximum.

blender_version_min cached property

blender_version_min: str

This exact M.m.p version is the minimum.

official_sources cached property

official_sources: list[BLVersionSourceOfficial]

All official sources in self.sources.

unofficial_sources cached property

unofficial_sources: frozenset[BLVersionSource]

All non-official sources in self.sources.

version cached property

version: str

Deduce the range of Blender version sources.


blext.extyp.blext_location

Implements the concept of "extension location" via the BLExtLocation object.

BLExtLocation

Bases: BaseModel

An abstraction of "extension location".

Extension projects can be found in all kinds of locations
  • Paths: An extension contained within a script or project path.
  • HTTP URL: A script extension available on the internet.
  • Git Ref: A particular commit on some git repository.

This object handles the particulars of retrieving extension projects, and in turn, exposes standardied information such as: - Where can the extension specification be loaded from? - Which path can wheels be cached to?

Notes

The following methods must be overridden: - path_spec

ATTRIBUTE DESCRIPTION
path_global_project_cache

Folder where extension data can be placed. - Script extensions must have their data placed here, since they have no folder to write to. - Project extensions can have data cached here, if their directory can't/shouldn't be written to.

TYPE: Path

path_global_download_cache

Folder where downloaded files can be placed.

TYPE: Path

force_global_project_cache

Force the use of the global project cache, even when a local project cache can be used.

TYPE: bool

is_project_extension cached property

is_project_extension: bool

Whether the specified extension is a project extension.

is_script_extension cached property

is_script_extension: bool

Whether the specified extension is a single-file script extension.

path_build_cache cached property

path_build_cache: Path

Pre-pack cache folder for this extension project.

Notes

When pre-packing extension zips, the pre-packed result is written to this folder.

path_prepack_cache cached property

path_prepack_cache: Path

Pre-pack cache folder for this extension project.

Notes

When pre-packing extension zips, the pre-packed result is written to this folder.

path_project_cache cached property

path_project_cache: Path

The project cache to use when building and/or managing this specification.

Notes

This folder is guaranteed to exist, and to be writable. No guarantees are made about the folder's contents, ex. source code or lockfiles.

path_spec cached property

path_spec: Path

Path to a file from which the extension specification can be loaded.

Notes

It is up to the implementation to define how this file comes about. For instance, the file might be searched for, downloaded, or even generated just in time.

The resulting file is only guaranteed to be theoretically capable of containing an extension specification. No guarantee is made here to whether the contents of this file can actually produce a valid extension specification.

See Also
  • See blext.spec.BLExtSpec for more on how a valid specification path is parsed.

path_uv_lock cached property

path_uv_lock: Path

Path to the blext project's uv.lock file.

path_wheel_cache cached property

path_wheel_cache: Path

Wheel cache folder for this extension project.

Notes

When pre-packing extension zips, the pre-packed result is written to this folder.

path_pysrc

path_pysrc(pkg_name: str) -> Path

Path to the extension source code.

PARAMETER DESCRIPTION
pkg_name

Name of the Blender extension. Only used when self.is_project_extension is True.

TYPE: str

RETURNS DESCRIPTION
Path
  • Project Extension: Path to a Python package directory.
Path
  • Script Extension: Path to a Python script.
Source code in blext/extyp/blext_location.py
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
def path_pysrc(self, pkg_name: str) -> Path:
	"""Path to the extension source code.

	Parameters:
		pkg_name: Name of the Blender extension.
			Only used when `self.is_project_extension` is `True`.

	Returns:
		- Project Extension: Path to a Python package directory.
		- Script Extension: Path to a Python script.
	"""
	if self.is_project_extension:
		package_path = self.path_spec.parent / pkg_name
		if package_path.is_dir():
			return self.path_spec.parent / pkg_name

		msg = f'The extension project name, {pkg_name}, does not have a Python package with the same name at: {self.path_spec.parent / pkg_name}'
		raise ValueError(msg)

	if self.is_script_extension:
		return self.path_spec

	msg = f"Extension project path ({self.path_spec}) is neither a script extension or a project extension. This shouldn't happen."
	raise RuntimeError(msg)

blext.extyp.blext_location_git

Implements the concept of "extension location" via the BLExtLocation object.

BLExtLocationGit

Bases: BLExtLocation

git repository location of a Blender extension.

ATTRIBUTE DESCRIPTION
url

URL of a git repository.

TYPE: HttpUrl

rev

Reference to a particular commit.

TYPE: str | None

tag

Reference to a particular tag.

TYPE: str | None

branch

Reference to the head of a particular branch.

TYPE: str | None

entrypoint

Path to an extension specification file, relative to the repository root.

TYPE: Path | None

path_spec cached property

path_spec: Path

Path to a file from which the extension specification can be loaded.

Notes

The specified git repository will be cloned, checked out, then searched for an extension spec.

See Also
  • See blext.spec.BLExtSpec for more on how a valid specification path is parsed.

check_only_one_ref classmethod

check_only_one_ref(data: Any) -> Any

Check that only one ref is given.

If only git_url is given, then branch is set to 'main'.

PARAMETER DESCRIPTION
data

Raw data passed to pydantic's model construction. Tends to be a dictionary.

TYPE: Any

RETURNS DESCRIPTION
Any

Altered and/or validated raw data for continuing pydantics model construction.

Source code in blext/extyp/blext_location_git.py
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
@pyd.model_validator(mode='before')
@classmethod
def check_only_one_ref(cls, data: typ.Any) -> typ.Any:  # pyright: ignore[reportAny]
	"""Check that only one ref is given.

	If only `git_url` is given, then `branch` is set to `'main'`.

	Parameters:
		data: Raw data passed to `pydantic`'s model construction.
			Tends to be a dictionary.

	Returns:
		Altered and/or validated raw data for continuing `pydantic`s model construction.
	"""
	if isinstance(data, dict):
		num_refs_that_are_none = sum(
			1 if data.get(attr) is not None else 0  # pyright: ignore[reportUnknownMemberType]
			for attr in ['rev', 'tag', 'branch']
		)

		if num_refs_that_are_none == 0:
			data['branch'] = 'main'
			return data  # pyright: ignore[reportUnknownVariableType]

		if num_refs_that_are_none > 1:
			msg = f'Only one `git` reference can be given, but {num_refs_that_are_none} were found (data={data})'
			raise ValueError(msg)

	return data  # pyright: ignore[reportUnknownVariableType]

blext.extyp.blext_location_http

Implements the concept of "extension location" via the BLExtLocation object.

BLExtLocationHttp

Bases: BLExtLocation

Internet location of a Blender extension.

ATTRIBUTE DESCRIPTION
url

URL of a script extension.

TYPE: HttpUrl

path_spec cached property

path_spec: Path

Path to a file from which the extension specification can be loaded.

Notes

The specified git repository will be cloned, checked out, then searched for an extension spec.

See Also
  • See blext.spec.BLExtSpec for more on how a valid specification path is parsed.

blext.extyp.blext_location_path

Implements BLExtLocationPath.

BLExtLocationPath

Bases: BLExtLocation

Local filesystem location of a Blender extension.

ATTRIBUTE DESCRIPTION
path

Path to a Blender extension project. None indicates that the current working directory should be used.

TYPE: Path | None

path_spec cached property

path_spec: Path

Path to a file from which the extension specification can be loaded.


blext.extyp.log_level

Extension types serving as meaningful abstractions for managing Blender extensions.

ATTRIBUTE DESCRIPTION
ValidBLExtPerms

Hardcoded list of valid extension permissions. - files: Access to any filesystem operations. - network: Access to the internet. - clipboard: Permission to read and/or write the system clipboard. - camera: Permission to capture photos or videos from system sources. - microphone: Permission to capture audio from system sources.

ValidBLTags

Hardcoded list of valid extension tags.

BLExtLogLevel

Bases: StrEnum

Enumeration mapping strings to logging.* log levels from the standard library.

log_level property

log_level: int

The integer corresponding to each string log-level.

Derived from the logging module of the standard library.


blext.extyp.release_profile

Extension types serving as meaningful abstractions for managing Blender extensions.

ATTRIBUTE DESCRIPTION
ValidBLExtPerms

Hardcoded list of valid extension permissions. - files: Access to any filesystem operations. - network: Access to the internet. - clipboard: Permission to read and/or write the system clipboard. - camera: Permission to capture photos or videos from system sources. - microphone: Permission to capture audio from system sources.

ValidBLTags

Hardcoded list of valid extension tags.

ReleaseProfile

Bases: BaseModel

Settings baked into an extension, available from before register() is called.

"Release Profiles" give extension developers a way to make a "release" and/or "debug" version of an extension, in order to do things like:

  • Preconfigure file-based logging in a "debug-release" version, making it easy to gather information from users.
  • Run a "dev" version of the addon for development, ex. to include a web-server that hot-reloads the addon.
  • Run a "test" version of the addon for development, ex. to include a web-server that hot-reloads the addon.
Notes

For now, release profiles are quite limited:

  • Possible options are hard-coded into blext; currently, this only includes the ability to set logging fields.
  • Options cannot change an extension specification, beyond hard-coded options.
  • Extensions must explicitly load init_settings.toml and use the fields within.

export_init_settings

export_init_settings(
	*, fmt: Literal['json', 'toml']
) -> str

Initialization settings for this release profile, as a string.

PARAMETER DESCRIPTION
fmt

String format to export initialization settings to.

TYPE: Literal['json', 'toml']

RETURNS DESCRIPTION
str

Initialization settings as a formatted string.

Source code in blext/extyp/release_profile.py
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
def export_init_settings(self, *, fmt: typ.Literal['json', 'toml']) -> str:
	"""Initialization settings for this release profile, as a string.

	Parameters:
		fmt: String format to export initialization settings to.

	Returns:
		Initialization settings as a formatted string.
	"""
	# Parse Model to JSON
	## - JSON is used like a Rosetta Stone, since it is best supported by pydantic.
	json_str = self.model_dump_json(
		include={
			'use_log_file',
			'log_file_name',
			'log_file_level',
			'use_log_console',
			'log_console_level',
		},
		by_alias=True,
	)

	# Return String as Format
	if fmt == 'json':
		return json_str
	if fmt == 'toml':
		json_dict: dict[str, typ.Any] = json.loads(json_str)
		return tomli_w.dumps(json_dict)

	msg = f'Cannot export init settings to the given unknown format: {fmt}'  # pyright: ignore[reportUnreachable]
	raise ValueError(msg)

StandardReleaseProfile

Bases: StrEnum

Strings identifying standardized release profiles, for which a default ReleaseProfile object is available.

release_profile cached property

release_profile: ReleaseProfile

A sensible default for common release profiles.


blext.extyp.spdx_license

Convenient list of SPDX licenses.


blext.extyp.validators

Functions that help validate.

all_version_numbers_are_digits

all_version_numbers_are_digits(s: str) -> bool

Whether s, a version, has all-digit numbers.

Source code in blext/extyp/validators.py
81
82
83
def all_version_numbers_are_digits(s: str) -> bool:
	"""Whether `s`, a version, has all-digit numbers."""
	return all(number_str.isdigit() for number_str in s.split('.'))

blender_version_is_gt_4_2

blender_version_is_gt_4_2(s: str) -> bool

Whether s, a Blender version, is greater than 4.2.

Source code in blext/extyp/validators.py
86
87
88
89
def blender_version_is_gt_4_2(s: str) -> bool:
	"""Whether `s`, a Blender version, is greater than `4.2`."""
	version_tuple = tuple(int(number_str) for number_str in s.split('.'))
	return (version_tuple[0] == 4 and version_tuple[1] >= 2) or version_tuple[0] > 4  # noqa: PLR2004
is_copyright_name_valid(s: str) -> bool

Whether s, a copyright statement, has a valid copyright name.

Source code in blext/extyp/validators.py
102
103
104
105
106
def is_copyright_name_valid(s: str) -> bool:
	"""Whether `s`, a copyright statement, has a valid copyright name."""
	_, name = s.partition(' ')[0::2]

	return bool(name.strip())
is_copyright_year_valid(s: str) -> bool

Whether s, a copyright statement, has a valid copyright year.

Source code in blext/extyp/validators.py
92
93
94
95
96
97
98
99
def is_copyright_year_valid(s: str) -> bool:
	"""Whether `s`, a copyright statement, has a valid copyright year."""
	year = s.partition(' ')[0]
	year_split = year.partition('-')

	if year_split[1]:
		return year_split[0].isdigit() and year_split[2].isdigit()
	return year.isdigit()

is_str_strip_a_noop

is_str_strip_a_noop(s: str) -> bool

Whether s.strip() is equal to s.

Source code in blext/extyp/validators.py
61
62
63
def is_str_strip_a_noop(s: str) -> bool:
	"""Whether `s.strip()` is equal to `s`."""
	return s == s.strip()

is_str_strip_not_empty

is_str_strip_not_empty(s: str) -> bool

Whether s.strip() is an empty string.

Source code in blext/extyp/validators.py
56
57
58
def is_str_strip_not_empty(s: str) -> bool:
	"""Whether `s.strip()` is an empty string."""
	return bool(s.strip())

is_valid_bl_semver

is_valid_bl_semver(s: str) -> bool

Whether s is valid semver, as defined by Blender.

Source code in blext/extyp/validators.py
51
52
53
def is_valid_bl_semver(s: str) -> bool:
	"""Whether `s` is valid semver, as defined by Blender."""
	return _BL_SEMVER_PATTERN.search(s) is not None

last_char_is_alphanum_or_closes_bracket

last_char_is_alphanum_or_closes_bracket(s: str) -> bool

Whether the last character of s is either alphanumeric, or ends with ), ], or }.

Source code in blext/extyp/validators.py
74
75
76
77
78
def last_char_is_alphanum_or_closes_bracket(s: str) -> bool:
	"""Whether the last character of `s` is either alphanumeric, or ends with `)`, `]`, or `}`."""
	if s:
		return s[-1].isalnum() or s[-1] in [')', ']', '}']
	return False

lowercase_wheel_filename_endswith_whl

lowercase_wheel_filename_endswith_whl(s: str) -> bool

Whether s, a wheel filename, ends with whl or WHL.

Source code in blext/extyp/validators.py
125
126
127
def lowercase_wheel_filename_endswith_whl(s: str) -> bool:
	"""Whether `s`, a wheel filename, ends with `whl` or `WHL`."""
	return os.path.basename(s).lower().endswith('whl')  # noqa: PTH119

no_dunder_in_string

no_dunder_in_string(s: str) -> bool

Whether __ is in s.

Source code in blext/extyp/validators.py
26
27
28
def no_dunder_in_string(s: str) -> bool:
	"""Whether `__` is in `s`."""
	return '__' not in s

no_str_endswith_underscore

no_str_endswith_underscore(s: str) -> bool

Whether s ends with _.

Source code in blext/extyp/validators.py
36
37
38
def no_str_endswith_underscore(s: str) -> bool:
	"""Whether `s` ends with `_`."""
	return not s.endswith('_')

no_str_startswith_underscore

no_str_startswith_underscore(s: str) -> bool

Whether s starts with _.

Source code in blext/extyp/validators.py
31
32
33
def no_str_startswith_underscore(s: str) -> bool:
	"""Whether `s` starts with `_`."""
	return not s.startswith('_')

str_has_no_bl_control_chars

str_has_no_bl_control_chars(s: str) -> bool

Whether s contains any control characters, as defined by Blender.

Source code in blext/extyp/validators.py
69
70
71
def str_has_no_bl_control_chars(s: str) -> bool:
	"""Whether `s` contains any control characters, as defined by Blender."""
	return len(list(_BL_CONTROL_CHARS.finditer(s))) == 0

wheel_filename_has_no_backward_slashes

wheel_filename_has_no_backward_slashes(s: str) -> bool

Whether the input wheel filename string has any backward slash characters.

Notes

Equivalent to a blender_ext.py snippet.

It should be noted that the error message in this snippet claims to be checking for "forward slashes", when in fact it checks for backslash characters.

Source code in blext/extyp/validators.py
114
115
116
117
118
119
120
121
122
def wheel_filename_has_no_backward_slashes(s: str) -> bool:
	"""Whether the input wheel filename string has any backward slash characters.

	Notes:
		Equivalent to a [`blender_ext.py` snippet](https://projects.blender.org/blender/blender/src/commit/a51f293548adba0dda086f5771401e8d8ab66227/scripts/addons_core/bl_pkg/cli/blender_ext.py#L1729).

		It should be noted that the error message in this snippet claims to be checking for "forward slashes", when in fact it checks for backslash characters.
	"""
	return '\\' not in s

wheel_filename_has_no_double_quotes

wheel_filename_has_no_double_quotes(s: str) -> bool

Whether s, a wheel filename, contains double quotes.

Source code in blext/extyp/validators.py
109
110
111
def wheel_filename_has_no_double_quotes(s: str) -> bool:
	"""Whether `s`, a wheel filename, contains double quotes."""
	return r'"' not in s

wheel_filename_has_valid_number_of_dashes

wheel_filename_has_valid_number_of_dashes(s: str) -> bool

Whether s, a wheel filename, has the correct number of -s expected of a valid wheel filename.

Source code in blext/extyp/validators.py
130
131
132
def wheel_filename_has_valid_number_of_dashes(s: str) -> bool:
	"""Whether `s`, a wheel filename, has the correct number of `-`s expected of a valid wheel filename."""
	return len(os.path.basename(s).split('-')) in [5, 6]  # noqa: PTH119