feat: blob of fixes and features
parent
431aec6400
commit
dc9a080996
|
@ -5,17 +5,17 @@ from .gather import BLExtInfo, blext_info
|
|||
from .utils import bl_logger as log
|
||||
|
||||
__all__ = [
|
||||
'BLExtInfo',
|
||||
'BLField',
|
||||
'bl_prop',
|
||||
'blext_dir',
|
||||
'blext_info',
|
||||
'blext_prefs',
|
||||
'gather',
|
||||
'init',
|
||||
'log',
|
||||
'log',
|
||||
'prefs',
|
||||
'registration',
|
||||
'typ',
|
||||
'blext_dir',
|
||||
'blext_prefs',
|
||||
'BLField',
|
||||
'BLExtInfo',
|
||||
'log',
|
||||
'blext_info',
|
||||
'bl_logger',
|
||||
]
|
||||
|
|
|
@ -5,10 +5,10 @@ from .bl_prop import BLProp, SupportedBLPropType
|
|||
from .signal import Signal
|
||||
|
||||
__all__ = [
|
||||
'types',
|
||||
'BLField',
|
||||
'BLIDProps',
|
||||
'BLProp',
|
||||
'SupportedBLPropType',
|
||||
'Signal',
|
||||
'SupportedBLPropType',
|
||||
'types',
|
||||
]
|
||||
|
|
|
@ -23,12 +23,9 @@ In particular,
|
|||
import functools
|
||||
import typing as typ
|
||||
|
||||
import bpy
|
||||
|
||||
# import griffe
|
||||
from .bl_id_props import BLIDProps
|
||||
from .bl_prop import BLProp, SupportedBLPropType
|
||||
from .signal import Signal
|
||||
|
||||
|
||||
####################
|
||||
|
@ -44,7 +41,7 @@ class BLField:
|
|||
display_name: str | None = None,
|
||||
description: str | None = None,
|
||||
**bl_prop_info: dict[str, typ.Any],
|
||||
):
|
||||
) -> None:
|
||||
"""Initializes and sets the attribute to a given default value.
|
||||
|
||||
The attribute **must** declare a type annotation, and it **must** match the type of `default_value`.
|
||||
|
@ -63,35 +60,6 @@ class BLField:
|
|||
####################
|
||||
# - Initialization
|
||||
####################
|
||||
def init_on_bl_class(self, bl_class: type[SupportedBLPropType]) -> None:
|
||||
# Add 'draw_blfield' Method
|
||||
## - This enables the use of BLProp-specific draw() methods.
|
||||
if not hasattr(bl_class, f'blfield__{self.name}'):
|
||||
setattr(
|
||||
bl_class,
|
||||
f'blfield__{self.name}',
|
||||
property(lambda bl_instance: self),
|
||||
)
|
||||
if not hasattr(bl_class, 'draw_blfield'):
|
||||
|
||||
def draw_blfield(
|
||||
bl_instance: SupportedBLPropType,
|
||||
layout: bpy.types.UILayout,
|
||||
bl_field_name: str,
|
||||
**kwargs: dict[str, typ.Any],
|
||||
) -> None:
|
||||
# Find BLField Descriptor on BLInstance
|
||||
bl_field = getattr(bl_instance, f'blfield__{bl_field_name}', None)
|
||||
|
||||
# Call draw() of BLProp
|
||||
if bl_field is not None:
|
||||
bl_field.bl_prop.draw(bl_instance, layout, **kwargs)
|
||||
else:
|
||||
msg = f"A BLField with the name '{bl_field_name}' could not be found on the BLInstance '{bl_instance!s}'"
|
||||
raise ValueError(msg)
|
||||
|
||||
bl_class.draw_blfield = draw_blfield
|
||||
|
||||
def __set_name__(self, owner: type[SupportedBLPropType], name: str) -> None:
|
||||
"""Sets up this descriptor on the class, preparing it for per-instance use.
|
||||
|
||||
|
@ -106,8 +74,10 @@ class BLField:
|
|||
owner: The class that contains an attribute assigned to an instance of this descriptor.
|
||||
name: The name of the attribute that an instance of descriptor was assigned to.
|
||||
"""
|
||||
# Parse BLProp Information
|
||||
## - self.bl_prop is only available after this is done.
|
||||
####################
|
||||
# - Parse Information for BLProp
|
||||
####################
|
||||
## *self.bl_prop is only valid to use after this section runs.
|
||||
if issubclass(
|
||||
owner,
|
||||
BLIDProps | SupportedBLPropType,
|
||||
|
@ -139,12 +109,13 @@ class BLField:
|
|||
msg = f"Cannot declare 'BLField' (name='{name}') on unsupported class '{owner}'."
|
||||
raise TypeError(msg)
|
||||
|
||||
# Initialize BLProp on Owner
|
||||
## - Don't initialize a BLField on BLIDProps, since it modifies a global bpy type.
|
||||
## - Instead, its register() method must do the same sequence.
|
||||
####################
|
||||
# - Initialize Owner
|
||||
####################
|
||||
## This is only done if the owner should directly accessed at this point.
|
||||
## Ex. BLIDProps has its own register() method.
|
||||
if issubclass(owner, SupportedBLPropType):
|
||||
self.bl_prop.init_on_bl_class(owner)
|
||||
self.init_on_bl_class(owner)
|
||||
|
||||
@functools.cached_property
|
||||
def bl_prop(self) -> BLProp:
|
||||
|
@ -194,27 +165,7 @@ class BLField:
|
|||
bl_instance: Instance that is accessing the attribute.
|
||||
owner: The class that owns the instance.
|
||||
"""
|
||||
# Read the Non-Persistent Cached Value
|
||||
## - The value must exist in the thread-safe memory cache.
|
||||
cached_value = self.bl_prop.read_nonpersist(bl_instance)
|
||||
|
||||
# Check Non-Persistent Cache Status
|
||||
## - Signals are guaranteed not to overlap with any other object.
|
||||
if cached_value is Signal.CacheNotReady or cached_value is Signal.CacheEmpty:
|
||||
# Check if Blender Class Instance Exists
|
||||
if bl_instance is not None:
|
||||
# Read Persisted Value from Blender Class Instance
|
||||
## - The value on the BLInstance can persist across runs.
|
||||
persisted_value = self.bl_prop.read_persist(bl_instance)
|
||||
|
||||
# Fill Non-Persistent Cache
|
||||
## - The next read will be from a thread-safe memory cache.
|
||||
## - This aggressive caching minimizes traffic on `bpy`.
|
||||
self.bl_prop.write_nonpersist(bl_instance, persisted_value)
|
||||
|
||||
return persisted_value
|
||||
return self.bl_prop.default_value ## TODO: Good idea?
|
||||
return cached_value
|
||||
return self.bl_prop.read(bl_instance)
|
||||
|
||||
def __set__(self, bl_instance: SupportedBLPropType | None, value: typ.Any) -> None:
|
||||
"""Sets the value described by the BLField.
|
||||
|
@ -230,23 +181,4 @@ class BLField:
|
|||
bl_instance: Instance that is accessing the attribute.
|
||||
owner: The class that owns the instance.
|
||||
"""
|
||||
match value:
|
||||
# Signal: Invalidate Non-Persistent Cache
|
||||
case Signal.InvalidateCache:
|
||||
self.bl_prop.invalidate_nonpersist(bl_instance)
|
||||
|
||||
# Signal: BLProp-Specific
|
||||
case (
|
||||
signal
|
||||
) if value is Signal.ResetEnumItems or value is Signal.ResetStrSearch:
|
||||
self.bl_prop.handle_signal(signal)
|
||||
|
||||
# Signal: Invalid
|
||||
case Signal.CacheNotReady | Signal.CacheEmpty:
|
||||
msg = 'Cannot set BLField to `CacheEmpty` or `CacheNotReady` signal.'
|
||||
raise ValueError(msg)
|
||||
|
||||
# General Case:
|
||||
case _:
|
||||
# Invalidate Persistent AND Non-Persistent Cache
|
||||
self.bl_prop.write_persist(bl_instance, value)
|
||||
self.bl_prop.write(bl_instance, value)
|
||||
|
|
|
@ -32,9 +32,11 @@ SupportedBLPropType: typ.TypeAlias = (
|
|||
)
|
||||
|
||||
|
||||
####################
|
||||
# - Blender Property (Abstraction)
|
||||
####################
|
||||
def bl_prop_instance_property_name(bl_prop_name: str) -> str:
|
||||
"""Deduces the internal attribute name where a reference to this class instance will be persisted."""
|
||||
return f'blprop_instance__{bl_prop_name}'
|
||||
|
||||
|
||||
class BLProp(pyd.BaseModel, frozen=True):
|
||||
"""Convenient, fast, and safe interface to a property on a Blender class.
|
||||
|
||||
|
@ -76,7 +78,9 @@ class BLProp(pyd.BaseModel, frozen=True):
|
|||
|
||||
default_value: typ.Any
|
||||
|
||||
# TODO: Support certain property flags universally.
|
||||
use_property_group: bool = False
|
||||
|
||||
# TODO: Support certain property flags universally?
|
||||
|
||||
####################
|
||||
# - Computed
|
||||
|
@ -86,70 +90,10 @@ class BLProp(pyd.BaseModel, frozen=True):
|
|||
"""Deduces the internal attribute name where the raw Blender property data will be persisted."""
|
||||
return f'blprop__{self.name}'
|
||||
|
||||
####################
|
||||
# - Type Support
|
||||
####################
|
||||
@classmethod
|
||||
def supports_type_hint(cls, field_type_hint: type) -> bool:
|
||||
"""Cheaply deduce whether this BLProp implements the given type."""
|
||||
raise NotImplementedError
|
||||
|
||||
####################
|
||||
# - bpy.props Interface
|
||||
####################
|
||||
@property
|
||||
def bpy_prop_type(self) -> type[bpy.props.FloatProperty]:
|
||||
"""The underlying `bpy.props` class backing this property."""
|
||||
raise NotImplementedError
|
||||
|
||||
@property
|
||||
def bpy_prop_kwargs(self) -> dict[str, typ.Any]:
|
||||
"""Keyword arguments to pass to `self.bpy_prop_type`."""
|
||||
raise NotImplementedError
|
||||
|
||||
####################
|
||||
# - Encode/Decode
|
||||
####################
|
||||
def encode(self, value: typ.Any) -> typ.Any:
|
||||
"""Encode a value for compatibility with this Blender property, using the encapsulated types."""
|
||||
raise NotImplementedError
|
||||
|
||||
def decode(self, value: typ.Any) -> typ.Any:
|
||||
"""Encode a value for compatibility with this Blender property, using the encapsulated types."""
|
||||
raise NotImplementedError
|
||||
|
||||
def handle_signal(self, signal: Signal) -> None:
|
||||
"""Handle a signal in a manner specific to a Blender property."""
|
||||
raise NotImplementedError
|
||||
|
||||
####################
|
||||
# - UI
|
||||
####################
|
||||
def draw(
|
||||
self,
|
||||
bl_instance: SupportedBLPropType,
|
||||
layout: bpy.types.UILayout,
|
||||
) -> None:
|
||||
"""Draw this property to the given Blender `UILayout`.
|
||||
|
||||
Notes:
|
||||
Can and should be overridden by subclasses.
|
||||
Consider this a sensible default.
|
||||
"""
|
||||
## TODO:
|
||||
## - Universal support for icons?
|
||||
layout.prop(bl_instance, self.bl_name, text=self.display_name)
|
||||
|
||||
####################
|
||||
# - Selection
|
||||
####################
|
||||
@classmethod
|
||||
def priority(cls) -> int:
|
||||
"""The priority with which a type hint will resolve to this implementation in case of a conflict.
|
||||
|
||||
Lower implies higher priority.
|
||||
"""
|
||||
raise NotImplementedError
|
||||
@functools.cached_property
|
||||
def instance_bl_name(self):
|
||||
"""Deduces the internal attribute name where a reference to this class instance will be persisted."""
|
||||
return bl_prop_instance_property_name(self.name)
|
||||
|
||||
####################
|
||||
# - Creation
|
||||
|
@ -207,6 +151,7 @@ class BLProp(pyd.BaseModel, frozen=True):
|
|||
self,
|
||||
bl_class: type[SupportedBLPropType],
|
||||
) -> None:
|
||||
"""Prepare a Blender class to use this BLField"""
|
||||
"""Declare the Blender property on a Blender class, ensuring that the property will be available to all `SupportedBLPropType` respecting instances of that class.
|
||||
|
||||
Parameters:
|
||||
|
@ -214,22 +159,34 @@ class BLProp(pyd.BaseModel, frozen=True):
|
|||
**Must** be chosen such that `BLPropType.from_type(obj_type) == self`.
|
||||
"""
|
||||
|
||||
# Add Method: Reset Instance ID
|
||||
def reset_instance_id(bl_instance: SupportedBLPropType) -> None:
|
||||
bl_instance.instance_id = str(uuid.uuid4())
|
||||
####################
|
||||
# - Add Method: reset_instance_id
|
||||
####################
|
||||
if not hasattr(bl_class, 'reset_instance_id'):
|
||||
|
||||
bl_class.reset_instance_id = reset_instance_id
|
||||
def reset_instance_id(bl_instance: SupportedBLPropType) -> None:
|
||||
bl_instance.instance_id = str(uuid.uuid4())
|
||||
|
||||
# ID: Dynamic Assignment to Class Variable
|
||||
bl_class.reset_instance_id = reset_instance_id
|
||||
|
||||
####################
|
||||
# - [bpy.types.ID] Add Properties: instance_id & self.bpy_prop
|
||||
####################
|
||||
if issubclass(bl_class, bpy.types.ID):
|
||||
# Set Instance ID
|
||||
if not hasattr(bl_class, 'instance_id'):
|
||||
bl_class.instance_id = bpy.props.StringProperty()
|
||||
|
||||
# Set Property
|
||||
setattr(bl_class, self.bl_name, self.bpy_prop)
|
||||
if not hasattr(bl_class, self.bl_name):
|
||||
setattr(bl_class, self.bl_name, self.bpy_prop)
|
||||
else:
|
||||
msg = f'Tried to set attribute {self.bl_name} on `bl_class={bl_class}`, but the attribute already exists.'
|
||||
raise RuntimeError(msg)
|
||||
|
||||
# bpy_struct: Static Assignment to Class Annotations
|
||||
####################
|
||||
# - [bpy_struct] Add Properties: instance_id & self.bpy_prop
|
||||
####################
|
||||
elif issubclass(
|
||||
bl_class,
|
||||
bpy.types.AddonPreferences
|
||||
|
@ -244,8 +201,116 @@ class BLProp(pyd.BaseModel, frozen=True):
|
|||
# Set Annotation
|
||||
bl_class.__annotations__[self.bl_name] = self.bpy_prop
|
||||
|
||||
####################
|
||||
# - Add Attribute: {self.class_bl_name}
|
||||
####################
|
||||
if not hasattr(bl_class, self.instance_bl_name):
|
||||
setattr(
|
||||
bl_class,
|
||||
self.instance_bl_name,
|
||||
classmethod(lambda _bl_class: self),
|
||||
)
|
||||
## This may look a little funky. That's because it is a little funky.
|
||||
## This trick lets gets the actual 'BLProp' instance 'self' from a 'bl_instance'
|
||||
## 'self' gets bound within the lambda returned by the classmethod.
|
||||
## In this way, we can access the BLProp underlying any BLField.
|
||||
else:
|
||||
msg = 'Cannot register two identically named BLProps on the same BLClass.'
|
||||
raise ValueError(msg)
|
||||
|
||||
####################
|
||||
# - Add Method: draw_blprop
|
||||
####################
|
||||
if not hasattr(bl_class, 'draw_blprop'):
|
||||
|
||||
def draw_blprop(
|
||||
bl_instance: SupportedBLPropType,
|
||||
layout: bpy.types.UILayout,
|
||||
bl_prop_name: str,
|
||||
**kwargs: dict[str, typ.Any],
|
||||
) -> None:
|
||||
"""Find a `BLProp` defined on a Blender instance, and call its `.draw()` method.
|
||||
|
||||
Parameters:
|
||||
bl_instance: An instance of a Blender class, which has been given a `BLProp`.
|
||||
Since this is a method, this takes the place of `self`; that is to say, calling `self.draw_blprop` will set this parameter.
|
||||
layout: A Blender layout to draw to.
|
||||
class_bl_name: A Blender layout to draw to.
|
||||
"""
|
||||
# Find 'self' on BLInstance
|
||||
## The same draw_blprop must work for ALL BLProps with the same logic.
|
||||
## Thus, 'self.*' would absolutely not work.
|
||||
bl_prop = getattr(
|
||||
bl_instance, bl_prop_instance_property_name(bl_prop_name), None
|
||||
)
|
||||
if bl_prop is not None and isinstance(bl_prop, BLProp):
|
||||
bl_prop.draw(bl_instance, layout, **kwargs)
|
||||
else:
|
||||
msg = f"A BLProp instance could not be found at '{bl_prop_name}' on the BLInstance: '{bl_instance!s}'"
|
||||
raise ValueError(msg)
|
||||
|
||||
bl_class.draw_blprop = draw_blprop
|
||||
|
||||
####################
|
||||
# - Instance Methods
|
||||
# - High-Level Cache Semantics
|
||||
####################
|
||||
def read(self, bl_instance: SupportedBLPropType | None) -> typ.Any:
|
||||
"""A sensible choice of cache semantics for retrieving fields described by this `BLProp`.
|
||||
|
||||
Parameters:
|
||||
bl_instance: Instance that is accessing the attribute.
|
||||
"""
|
||||
# Read the Non-Persistent Cached Value
|
||||
## - The value must exist in the thread-safe memory cache.
|
||||
cached_value = self.read_nonpersist(bl_instance)
|
||||
|
||||
# Check Non-Persistent Cache Status
|
||||
## - Signals are guaranteed not to overlap with any other object.
|
||||
if cached_value is Signal.CacheNotReady or cached_value is Signal.CacheEmpty:
|
||||
# Check if Blender Class Instance Exists
|
||||
if bl_instance is not None:
|
||||
# Read Persisted Value from Blender Class Instance
|
||||
## - The value on the BLInstance can persist across runs.
|
||||
persisted_value = self.read_persist(bl_instance)
|
||||
|
||||
# Fill Non-Persistent Cache
|
||||
## - The next read will be from a thread-safe memory cache.
|
||||
## - This aggressive caching minimizes traffic on `bpy`.
|
||||
self.write_nonpersist(bl_instance, persisted_value)
|
||||
|
||||
return persisted_value
|
||||
return self.default_value ## TODO: Good idea?
|
||||
return cached_value
|
||||
|
||||
def write(self, bl_instance: SupportedBLPropType | None, value: typ.Any) -> None:
|
||||
"""A sensible choice of cache semantics for writing fields described by this `BLProp`.
|
||||
|
||||
Parameters:
|
||||
bl_instance: Instance that is accessing the attribute.
|
||||
"""
|
||||
match value:
|
||||
# Signal: Invalidate Non-Persistent Cache
|
||||
case Signal.InvalidateCache:
|
||||
self.invalidate_nonpersist(bl_instance)
|
||||
|
||||
# Signal: BLProp-Specific
|
||||
case signal if (
|
||||
value is Signal.ResetEnumItems or value is Signal.ResetStrSearch
|
||||
):
|
||||
self.handle_signal(signal)
|
||||
|
||||
# Signal: Invalid
|
||||
case Signal.CacheNotReady | Signal.CacheEmpty:
|
||||
msg = 'Cannot set BLProp to `CacheEmpty` or `CacheNotReady` signal.'
|
||||
raise ValueError(msg)
|
||||
|
||||
# General Case:
|
||||
case _:
|
||||
# Invalidate Persistent AND Non-Persistent Cache
|
||||
self.write_persist(bl_instance, value)
|
||||
|
||||
####################
|
||||
# - Low-Level Cache Semantics
|
||||
####################
|
||||
def read_nonpersist(self, bl_instance: SupportedBLPropType | None) -> typ.Any:
|
||||
"""Read the non-persistent cache value for this property.
|
||||
|
@ -336,18 +401,34 @@ class BLProp(pyd.BaseModel, frozen=True):
|
|||
bl_instance: The Blender object to
|
||||
**NOTE**: `bl_instance` must not be `None`, as neighboring methods sometimes allow.
|
||||
"""
|
||||
signal = bl_prop_cache.write(
|
||||
bl_instance,
|
||||
key=self.bl_name,
|
||||
value=self.encode(value),
|
||||
use_nonpersist=False,
|
||||
use_persist=True,
|
||||
)
|
||||
if self.use_property_group:
|
||||
for subkey, subvalue in value.items():
|
||||
signal = bl_prop_cache.write(
|
||||
getattr(bl_instance, self.bl_name),
|
||||
key=subkey,
|
||||
value=subvalue,
|
||||
use_nonpersist=False,
|
||||
use_persist=True,
|
||||
)
|
||||
|
||||
if signal is Signal.CacheNotReady:
|
||||
msg = f"Tried to write value '{self.bl_name}={value}' to persistent cache of bl_instance '{bl_instance}', but the cache was not yet ready."
|
||||
raise ValueError(msg)
|
||||
if signal is Signal.CacheNotReady:
|
||||
msg = f"Tried to write value '{self.bl_name}={value}' to persistent cache of bl_instance '{bl_instance}', but the cache was not yet ready."
|
||||
raise ValueError(msg)
|
||||
else:
|
||||
signal = bl_prop_cache.write(
|
||||
bl_instance,
|
||||
key=self.bl_name,
|
||||
value=self.encode(value),
|
||||
use_nonpersist=False,
|
||||
use_persist=True,
|
||||
use_group=self.use_property_group,
|
||||
)
|
||||
|
||||
if signal is Signal.CacheNotReady:
|
||||
msg = f"Tried to write value '{self.bl_name}={value}' to persistent cache of bl_instance '{bl_instance}', but the cache was not yet ready."
|
||||
raise ValueError(msg)
|
||||
|
||||
# Write Entire Structure to Non-Persistent Cache
|
||||
self.write_nonpersist(bl_instance, value)
|
||||
|
||||
def invalidate_nonpersist(self, bl_instance: SupportedBLPropType | None) -> None:
|
||||
|
@ -361,3 +442,73 @@ class BLProp(pyd.BaseModel, frozen=True):
|
|||
bl_instance,
|
||||
self.bl_name,
|
||||
)
|
||||
|
||||
####################
|
||||
# - Overridable: UI
|
||||
####################
|
||||
def draw(
|
||||
self,
|
||||
bl_instance: SupportedBLPropType,
|
||||
layout: bpy.types.UILayout,
|
||||
text: str | None = None,
|
||||
) -> None:
|
||||
"""Draw this property to the given Blender `UILayout`.
|
||||
|
||||
Notes:
|
||||
Can and should be overridden by subclasses.
|
||||
Consider this a sensible default.
|
||||
"""
|
||||
## TODO:
|
||||
## - Universal support for icons?
|
||||
layout.prop(
|
||||
bl_instance,
|
||||
self.bl_name,
|
||||
text=self.display_name if text is None else text,
|
||||
)
|
||||
|
||||
####################
|
||||
# - Abstract: Type Hint Support
|
||||
####################
|
||||
@classmethod
|
||||
def supports_type_hint(cls, field_type_hint: type) -> bool:
|
||||
"""Cheaply deduce whether this BLProp implements the given type."""
|
||||
raise NotImplementedError
|
||||
|
||||
####################
|
||||
# - Abstract: bpy.props Interface
|
||||
####################
|
||||
@property
|
||||
def bpy_prop_type(self) -> type[bpy.props.FloatProperty]:
|
||||
"""The underlying `bpy.props` class backing this property."""
|
||||
raise NotImplementedError
|
||||
|
||||
@property
|
||||
def bpy_prop_kwargs(self) -> dict[str, typ.Any]:
|
||||
"""Keyword arguments to pass to `self.bpy_prop_type`."""
|
||||
raise NotImplementedError
|
||||
|
||||
####################
|
||||
# - Abstract: Encode/Decode
|
||||
####################
|
||||
def encode(self, value: typ.Any) -> typ.Any:
|
||||
"""Encode a value for compatibility with this Blender property, using the encapsulated types."""
|
||||
raise NotImplementedError
|
||||
|
||||
def decode(self, value: typ.Any) -> typ.Any:
|
||||
"""Encode a value for compatibility with this Blender property, using the encapsulated types."""
|
||||
raise NotImplementedError
|
||||
|
||||
def handle_signal(self, signal: Signal) -> None:
|
||||
"""Handle a signal in a manner specific to a Blender property."""
|
||||
raise NotImplementedError
|
||||
|
||||
####################
|
||||
# - Selection
|
||||
####################
|
||||
@classmethod
|
||||
def priority(cls) -> int:
|
||||
"""The priority with which a type hint will resolve to this implementation in case of a conflict.
|
||||
|
||||
Lower implies higher priority.
|
||||
"""
|
||||
raise NotImplementedError
|
||||
|
|
|
@ -1,11 +1,15 @@
|
|||
from .bool_prop import BoolProp
|
||||
from .float_prop import FloatProp
|
||||
from .int_prop import IntProp
|
||||
from .path_prop import PathProp
|
||||
from .str_enum_prop import StrEnumProp
|
||||
from .str_prop import StrProp
|
||||
|
||||
__all__ = [
|
||||
'BoolProp',
|
||||
'FloatProp',
|
||||
'IntProp',
|
||||
'PathProp',
|
||||
'StrEnumProp',
|
||||
'StrProp',
|
||||
]
|
||||
|
|
|
@ -91,6 +91,7 @@ class BoolProp(BLProp, frozen=True):
|
|||
self,
|
||||
bl_instance: SupportedBLPropType,
|
||||
layout: bpy.types.UILayout,
|
||||
text: str | None = None,
|
||||
toggle: bool = False,
|
||||
expand: bool = False,
|
||||
) -> None:
|
||||
|
@ -98,7 +99,7 @@ class BoolProp(BLProp, frozen=True):
|
|||
layout.prop(
|
||||
bl_instance,
|
||||
self.bl_name,
|
||||
text=self.display_name,
|
||||
text=self.display_name if text is None else text,
|
||||
toggle=toggle,
|
||||
expand=expand,
|
||||
)
|
||||
|
|
|
@ -24,25 +24,62 @@ import bpy
|
|||
|
||||
from blroots.utils import bl_instance, logger
|
||||
|
||||
from ..bl_prop import BLProp
|
||||
from ..bl_prop import BLProp, bl_prop_instance_property_name
|
||||
|
||||
log = logger.get(__name__)
|
||||
|
||||
PRIORITY = 11
|
||||
|
||||
## TODO: Main thing, I think, at this point, is to find a way to register those ProeprtyGroups mindfully, then go play with it!
|
||||
|
||||
|
||||
@typ.runtime_checkable
|
||||
class BLPropClass(typ.Protocol):
|
||||
@classmethod
|
||||
def bl_prop_group(cls) -> type[bpy.types.PropertyGroup]: ...
|
||||
|
||||
## TODO: The bl_prop_group should be checked for:
|
||||
## - That it is a pydantic BaseModel.
|
||||
## - That the BaseModel has frozen=True.
|
||||
## - That data attributes are identical between the BL_ class and this version.
|
||||
## - That type annotations on data attributes are identical.
|
||||
## TODO: USAGE
|
||||
## - This class can be used freely without a backing BL_ class.
|
||||
## - This class can also be used to type-annotate a BLField.
|
||||
## -- The BL_ class must be registered before being assigned to anything.
|
||||
## -- Since frozen=True, only the entire instance can be replaced at a time.
|
||||
## --- Underneath, a backing PropertyGroup will be mutated as needed.
|
||||
## -- bl_events must be propagated to the backing PropertyGroup.
|
||||
## --- Use bl_event.propagate with source='parent' to get data from the PropertyGroup's parent.
|
||||
## --- Let's make sure to make it easy for instances of PropertyGroup to get that parent!
|
||||
## --- We can later do something similar for data pipelines between nodes and sockets.
|
||||
|
||||
|
||||
# def to_bl_prop_group(
|
||||
# self, bl_prop_group: BL_LoggingPrefs
|
||||
# ) -> type[bpy.types.PropertyGroup]:
|
||||
# bl_prop_group.use_log_file = self.use_log_file
|
||||
# bl_prop_group.log_file_level = self.log_file_level
|
||||
# bl_prop_group.log_file_path = self.log_file_path
|
||||
#
|
||||
# bl_prop_group.use_log_console = self.use_log_console
|
||||
# bl_prop_group.log_console_level = self.log_console_level
|
||||
#
|
||||
# @classmethod
|
||||
# def from_bl_prop_group(cls, bl_prop_group: BL_LoggingPrefs) -> typ.Self:
|
||||
# return cls(
|
||||
# use_log_file=bl_prop_group.use_log_file,
|
||||
# log_file_level=bl_prop_group.log_file_level,
|
||||
# log_file_path=bl_prop_group.log_file_path,
|
||||
# use_log_console=bl_prop_group.use_log_console,
|
||||
# log_console_level=bl_prop_group.log_console_level,
|
||||
# )
|
||||
|
||||
|
||||
class ClassProp(BLProp, frozen=True):
|
||||
"""A single constrained floating-point number.
|
||||
"""A single class; in particular, an appropriately configured `pydantic` BaseModel."""
|
||||
|
||||
Parameters:
|
||||
abs_min: The absolute minimum value of the floating point number.
|
||||
abs_max: The absolute maximum value of the floating point number.
|
||||
soft_min: The lowest value that can be set using the UI slider.
|
||||
soft_max: The highest value that can be set using the UI slider.
|
||||
float_prec: The number of decimal places displayed in the UI.
|
||||
float_step: The size of each mouse-move increment when using the UI slider.
|
||||
_Divide this integer by `100` for actual change per increment._
|
||||
"""
|
||||
use_property_group = True
|
||||
|
||||
default_value: None
|
||||
|
||||
|
@ -51,17 +88,23 @@ class ClassProp(BLProp, frozen=True):
|
|||
####################
|
||||
@classmethod
|
||||
def supports_type_hint(cls, field_type_hint: type) -> bool:
|
||||
"""Support only the explicit type `float` aka. `builtins.float`."""
|
||||
return inspect.isclass(field_type_hint) and hasattr(
|
||||
field_type_hint, 'property_group'
|
||||
"""Support classes that conform to the `BLPropClass` protocol."""
|
||||
return inspect.isclass(field_type_hint) and isinstance(
|
||||
field_type_hint, BLPropClass
|
||||
)
|
||||
|
||||
@property
|
||||
def bpy_property_group(self) -> type[bpy.props.PropertyGroup]:
|
||||
"""Dynamically create a PropertyGroup."""
|
||||
## TODO: We can make it; the trouble is, who registers it, and when?
|
||||
## - Also, there's an underlying class instance
|
||||
pass
|
||||
return self.type_hint.bl_prop_group()
|
||||
## TODO: Who registers it, and when?
|
||||
## TODO: I don't like having the "two-class" paradigm.
|
||||
## - On the plus side, "outside classes" can be adapted.
|
||||
## - On the minus side, it's two things now. Two parameter documentations to parse, etc. .
|
||||
## - If we did go with an all-in-one, typ.Annotated w/partial BLField constructor?
|
||||
## - Or somehow get pydantic to understand setting it to a BLField?
|
||||
## - I honestly don't know what to do exactly...
|
||||
## - On the other hand hand, look how mindful and simple this method is.
|
||||
|
||||
####################
|
||||
# - bpy.props Interface
|
||||
|
@ -69,7 +112,7 @@ class ClassProp(BLProp, frozen=True):
|
|||
@property
|
||||
def bpy_prop_type(self) -> type[bpy.props.PointerProperty]:
|
||||
"""Use `bpy.props.PointerProperty` to reference a particular pre-made `bpy.types.PropertyGroup` subclass."""
|
||||
return bpy.props.PointerProperty # type: ignore[no-any-return]
|
||||
return bpy.props.PointerProperty
|
||||
|
||||
@functools.cached_property
|
||||
def bpy_prop_kwargs(self) -> dict[str, typ.Any]:
|
||||
|
@ -81,17 +124,48 @@ class ClassProp(BLProp, frozen=True):
|
|||
####################
|
||||
# - Encode/Decode
|
||||
####################
|
||||
def encode(self, value: typ.Any) -> float:
|
||||
"""Encode a value that supports `float()` to a Blender float."""
|
||||
def encode(self, value: typ.Any) -> dict[str, typ.Any]:
|
||||
"""Encode a value to a dictionary that will be written to a `PropertyGroup`.
|
||||
|
||||
Notes:
|
||||
The pickle is this:
|
||||
|
||||
- On the PropertyGroup, each field is associated with a unique BLProp instance.
|
||||
- During BLProp initialization, a class method is created to allow accessing the BLProp underlying any BLField.
|
||||
- Why do we need to do this? BLProp instance uses encode()/decode() to translate between user-facing and Blender-facing representations of data.
|
||||
- Therefore,for each subproperty, retrieve the `BLProp` instance, then use its `.encode()` method to transform `value` fields down to the Blender representation that will be expected by the `PropertyGroup`.
|
||||
- This naturally provides correct recursion, too, since `.encode()` will simply chain its way all the way down to the bottom.
|
||||
|
||||
"""
|
||||
if isinstance(value, self.type_hint):
|
||||
# TODO: Dump the model to a dictionary? How to handle the nested BLProps? Better to delegate somehow to the PropertyGroup?
|
||||
pass
|
||||
return {
|
||||
bl_prop_name: (
|
||||
bl_prop := getattr( # noqa: F841
|
||||
self.bpy_property_group,
|
||||
bl_prop_instance_property_name(bl_prop_name),
|
||||
)()
|
||||
).encode(getattr(value, bl_prop_name))
|
||||
for bl_prop_name in self.bpy_property_group.bl_props
|
||||
}
|
||||
|
||||
raise NotImplementedError
|
||||
|
||||
def decode(self, raw_bl_value: float) -> float:
|
||||
"""Decode a Blender float by doing nothing."""
|
||||
return raw_bl_value
|
||||
def decode(self, raw_bl_value: bpy.types.PropertyGroup) -> typ.Any:
|
||||
"""Decode the subclass of `bpy.types.PropertyGroup` subclass into the `pydantic.BaseModel` contained within `self.type_hint`.
|
||||
|
||||
Notes:
|
||||
Simply reading the `BLField`s invokes `.decode()` recursively and correctly.
|
||||
Thus, decoding ends up being comparatively simpler than `encode()`.
|
||||
"""
|
||||
if isinstance(raw_bl_value, self.bpy_property_group):
|
||||
return self.type_hint(
|
||||
**{
|
||||
bl_prop_name: getattr(raw_bl_value, bl_prop_name)
|
||||
for bl_prop_name in self.bpy_property_group.bl_props
|
||||
}
|
||||
)
|
||||
|
||||
raise NotImplementedError
|
||||
|
||||
####################
|
||||
# - UI
|
||||
|
|
|
@ -122,10 +122,16 @@ class FloatProp(BLProp, frozen=True):
|
|||
self,
|
||||
bl_instance: SupportedBLPropType,
|
||||
layout: bpy.types.UILayout,
|
||||
text: str | None = None,
|
||||
slider: bool = False,
|
||||
) -> None:
|
||||
"""Draw this property to the given Blender `UILayout`."""
|
||||
layout.prop(bl_instance, self.bl_name, text=self.display_name, slider=slider)
|
||||
layout.prop(
|
||||
bl_instance,
|
||||
self.bl_name,
|
||||
text=self.display_name if text is None else text,
|
||||
slider=slider,
|
||||
)
|
||||
|
||||
####################
|
||||
# - Selection
|
||||
|
|
|
@ -111,12 +111,16 @@ class IntProp(BLProp, frozen=True):
|
|||
self,
|
||||
bl_instance: SupportedBLPropType,
|
||||
layout: bpy.types.UILayout,
|
||||
text: str | None = None,
|
||||
slider: bool = False,
|
||||
) -> None:
|
||||
"""Draw this property to the given Blender `UILayout`."""
|
||||
## TODO:
|
||||
## - Support for slider
|
||||
layout.prop(bl_instance, self.bl_name, text=self.display_name, slider=slider)
|
||||
layout.prop(
|
||||
bl_instance,
|
||||
self.bl_name,
|
||||
text=self.display_name if text is None else text,
|
||||
slider=slider,
|
||||
)
|
||||
|
||||
####################
|
||||
# - Selection
|
||||
|
|
|
@ -0,0 +1,130 @@
|
|||
# blroots
|
||||
# Copyright (C) 2025 blroots Project Contributors
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU Affero General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU Affero General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU Affero General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
"""Implements `PathProp`."""
|
||||
|
||||
import builtins
|
||||
import functools
|
||||
import typing as typ
|
||||
from pathlib import Path
|
||||
|
||||
import bpy
|
||||
|
||||
from ..bl_prop import BLProp, SupportedBLPropType
|
||||
|
||||
PRIORITY = 20
|
||||
|
||||
|
||||
class PathProp(BLProp, frozen=True):
|
||||
"""A single constrained floating-point number.
|
||||
|
||||
Parameters:
|
||||
abs_min: The absolute minimum value of the floating point number.
|
||||
abs_max: The absolute maximum value of the floating point number.
|
||||
soft_min: The lowest value that can be set using the UI slider.
|
||||
soft_max: The highest value that can be set using the UI slider.
|
||||
float_prec: The number of decimal places displayed in the UI.
|
||||
float_step: The size of each mouse-move increment when using the UI slider.
|
||||
_Divide this integer by `100` for actual change per increment._
|
||||
"""
|
||||
|
||||
default_value: Path
|
||||
## TODO: Use 'before' field validator to select a default value when None is specified, then an 'after' field validator to constrain it to abs_min and abs_max.
|
||||
|
||||
# Constraints
|
||||
is_file: bool = False
|
||||
is_dir: bool = False
|
||||
## TODO: Validator that disallows both is_file and is_dir
|
||||
|
||||
## TODO: A "relative" constraint that uses '//'
|
||||
|
||||
####################
|
||||
# - Type Support
|
||||
####################
|
||||
@classmethod
|
||||
def supports_type_hint(cls, field_type_hint: type) -> bool:
|
||||
"""Support only the explicit type `float` aka. `builtins.float`."""
|
||||
return field_type_hint is Path
|
||||
|
||||
####################
|
||||
# - bpy.props Interface
|
||||
####################
|
||||
@property
|
||||
def bpy_prop_type(self) -> type[bpy.props.StringProperty]:
|
||||
"""The underlying `bpy.props` class backing this property."""
|
||||
return bpy.props.StringProperty # type: ignore[no-any-return]
|
||||
|
||||
@functools.cached_property
|
||||
def bpy_prop_kwargs(self) -> dict[str, typ.Any]:
|
||||
"""Keyword arguments to pass to `self.bpy_prop_type`."""
|
||||
kwargs: dict[str, typ.Any] = {
|
||||
'default': self.encode(self.default_value),
|
||||
}
|
||||
|
||||
if self.is_file:
|
||||
kwargs |= {'subtype': 'FILE_PATH'}
|
||||
|
||||
elif self.is_dir:
|
||||
kwargs |= {'subtype': 'DIR_PATH'}
|
||||
|
||||
else:
|
||||
kwargs |= {'subtype': 'FILE_PATH'}
|
||||
|
||||
return kwargs
|
||||
|
||||
####################
|
||||
# - Encode/Decode
|
||||
####################
|
||||
def encode(self, value: typ.Any) -> str:
|
||||
"""Encode a value that supports `float()` to a Blender float."""
|
||||
# Parse the Value
|
||||
if isinstance(value, Path):
|
||||
parsed_value = str(value.resolve())
|
||||
else:
|
||||
msg = f"Value '{value}' must be a Path."
|
||||
raise TypeError(msg)
|
||||
|
||||
return parsed_value
|
||||
|
||||
def decode(self, raw_bl_value: str) -> Path:
|
||||
"""Decode a Blender float by doing nothing."""
|
||||
return Path(bpy.path.abspath(raw_bl_value))
|
||||
|
||||
####################
|
||||
# - UI
|
||||
####################
|
||||
def draw(
|
||||
self,
|
||||
bl_instance: SupportedBLPropType,
|
||||
layout: bpy.types.UILayout,
|
||||
text: str | None = None,
|
||||
placeholder: str = '',
|
||||
) -> None:
|
||||
"""Draw this property to the given Blender `UILayout`."""
|
||||
layout.prop(
|
||||
bl_instance,
|
||||
self.bl_name,
|
||||
text=self.display_name if text is None else text,
|
||||
placeholder=placeholder,
|
||||
)
|
||||
|
||||
####################
|
||||
# - Selection
|
||||
####################
|
||||
@classmethod
|
||||
def priority(cls) -> int:
|
||||
"""Priority with which to prefer this implementation over others compatible with the same type hint."""
|
||||
return PRIORITY
|
|
@ -0,0 +1,123 @@
|
|||
# blroots
|
||||
# Copyright (C) 2025 blroots Project Contributors
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU Affero General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU Affero General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU Affero General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
"""Implements `StrEnumProp`."""
|
||||
|
||||
import enum
|
||||
import functools
|
||||
import typing as typ
|
||||
|
||||
import bpy
|
||||
|
||||
from ..bl_prop import BLProp, SupportedBLPropType
|
||||
|
||||
PRIORITY = 5
|
||||
|
||||
|
||||
class StrEnumProp(BLProp, frozen=True):
|
||||
"""A static enumeration."""
|
||||
|
||||
default_value: enum.StrEnum
|
||||
## TODO: Use 'before' field validator to select a default value when None is specified, then an 'after' field validator to constrain it to abs_min and abs_max.
|
||||
|
||||
####################
|
||||
# - Type Support
|
||||
####################
|
||||
@classmethod
|
||||
def supports_type_hint(cls, field_type_hint: type) -> bool:
|
||||
"""Support only the explicit type `float` aka. `builtins.float`."""
|
||||
return (
|
||||
issubclass(field_type_hint, enum.StrEnum)
|
||||
and hasattr(field_type_hint, 'bl_display_name')
|
||||
and hasattr(field_type_hint, 'bl_icon')
|
||||
)
|
||||
|
||||
####################
|
||||
# - bpy.props Interface
|
||||
####################
|
||||
@property
|
||||
def bpy_prop_type(self) -> type[bpy.props.EnumProperty]:
|
||||
"""The underlying `bpy.props` class backing this property."""
|
||||
return bpy.props.EnumProperty # type: ignore[no-any-return]
|
||||
|
||||
@functools.cached_property
|
||||
def bpy_prop_kwargs(self) -> dict[str, typ.Any]:
|
||||
"""Keyword arguments to pass to `self.bpy_prop_type`."""
|
||||
kwargs: dict[str, typ.Any] = {
|
||||
'items': [
|
||||
(
|
||||
el.name,
|
||||
el.bl_display_name,
|
||||
el.bl_display_name,
|
||||
el.bl_icon,
|
||||
idx,
|
||||
)
|
||||
for idx, el in enumerate(self.type_hint)
|
||||
],
|
||||
'default': self.encode(self.default_value),
|
||||
## TODO: Parse Enum.__doc__ to get description from the Attributes section.
|
||||
}
|
||||
|
||||
return kwargs
|
||||
|
||||
####################
|
||||
# - Encode/Decode
|
||||
####################
|
||||
def encode(self, value: typ.Any) -> enum.StrEnum:
|
||||
"""Encode a value that supports `float()` to a Blender float."""
|
||||
if isinstance(value, self.type_hint):
|
||||
parsed_value = value
|
||||
|
||||
elif isinstance(value, str):
|
||||
parsed_value = getattr(self.type_hint, value, None)
|
||||
if parsed_value is None:
|
||||
msg = f"Object '{value}' could not be coerced to the enum {self.type_hint} during encoding."
|
||||
raise ValueError(msg)
|
||||
|
||||
return parsed_value.name
|
||||
|
||||
def decode(self, raw_bl_value: str) -> str:
|
||||
"""Decode a Blender float by doing nothing."""
|
||||
parsed_value = getattr(self.type_hint, raw_bl_value, None)
|
||||
if parsed_value is not None:
|
||||
return raw_bl_value
|
||||
|
||||
msg = f"String '{raw_bl_value}' could not be coerced to the enum {self.type_hint} during decoding."
|
||||
raise ValueError(msg)
|
||||
|
||||
####################
|
||||
# - UI
|
||||
####################
|
||||
def draw(
|
||||
self,
|
||||
bl_instance: SupportedBLPropType,
|
||||
layout: bpy.types.UILayout,
|
||||
text: str | None = None,
|
||||
) -> None:
|
||||
"""Draw this property to the given Blender `UILayout`."""
|
||||
layout.prop(
|
||||
bl_instance,
|
||||
self.bl_name,
|
||||
text=self.display_name if text is None else text,
|
||||
)
|
||||
|
||||
####################
|
||||
# - Selection
|
||||
####################
|
||||
@classmethod
|
||||
def priority(cls) -> int:
|
||||
"""Priority with which to prefer this implementation over others compatible with the same type hint."""
|
||||
return PRIORITY
|
|
@ -28,16 +28,11 @@ PRIORITY = 20
|
|||
|
||||
|
||||
class StrProp(BLProp, frozen=True):
|
||||
"""A single constrained floating-point number.
|
||||
"""A single constrained string.
|
||||
|
||||
Parameters:
|
||||
abs_min: The absolute minimum value of the floating point number.
|
||||
abs_max: The absolute maximum value of the floating point number.
|
||||
soft_min: The lowest value that can be set using the UI slider.
|
||||
soft_max: The highest value that can be set using the UI slider.
|
||||
float_prec: The number of decimal places displayed in the UI.
|
||||
float_step: The size of each mouse-move increment when using the UI slider.
|
||||
_Divide this integer by `100` for actual change per increment._
|
||||
is_secret: Whether the string should be considered a secret value.
|
||||
Secrets display as dots in the interface, and aren't saved in the `.blend` on save.
|
||||
"""
|
||||
|
||||
default_value: str
|
||||
|
@ -101,11 +96,15 @@ class StrProp(BLProp, frozen=True):
|
|||
self,
|
||||
bl_instance: SupportedBLPropType,
|
||||
layout: bpy.types.UILayout,
|
||||
text: str | None = None,
|
||||
placeholder: str = '',
|
||||
) -> None:
|
||||
"""Draw this property to the given Blender `UILayout`."""
|
||||
layout.prop(
|
||||
bl_instance, self.bl_name, text=self.display_name, placeholder=placeholder
|
||||
bl_instance,
|
||||
self.bl_name,
|
||||
text=self.display_name if text is None else text,
|
||||
placeholder=placeholder,
|
||||
)
|
||||
|
||||
####################
|
||||
|
|
164
blroots/prefs.py
164
blroots/prefs.py
|
@ -1,164 +0,0 @@
|
|||
import logging
|
||||
import typing as typ
|
||||
from pathlib import Path
|
||||
|
||||
import bpy
|
||||
|
||||
from blroots import gather
|
||||
from blroots.bl_prop import BLField
|
||||
from blroots.utils import bl_logger
|
||||
|
||||
|
||||
####################
|
||||
# - User Preferences Mixin
|
||||
####################
|
||||
def LoggingPrefs(blext_pkg: str) -> type[type]:
|
||||
class _LoggingPrefs(bpy.types.AddonPreferences):
|
||||
"""Implements the `bl_logger` user preferences integration.
|
||||
|
||||
Notes:
|
||||
To check for the presence of this integration, use:
|
||||
```python
|
||||
getattr(addon_prefs, 'enable_integration__bl_logger', False)
|
||||
```
|
||||
"""
|
||||
|
||||
enable_integration__bl_logger: bool = True
|
||||
|
||||
####################
|
||||
# - Properties
|
||||
####################
|
||||
# Logging
|
||||
## File Logging
|
||||
use_log_file: bool = BLField(
|
||||
gather.blext_info(blext_pkg).use_log_file,
|
||||
display_name='Log to File',
|
||||
)
|
||||
# log_file_level: bl_logger.BLExtLogLevel = BLField(
|
||||
# gather.blext_info(blext_pkg).log_file_level,
|
||||
# display_name='File Log Level',
|
||||
# )
|
||||
# log_file_path: Path = BLField(
|
||||
# gather.blext_info(blext_pkg).log_file_path,
|
||||
# display_name='File Log Path',
|
||||
# )
|
||||
|
||||
## Console Logging
|
||||
use_log_console: bool = BLField(
|
||||
gather.blext_info(blext_pkg).use_log_console,
|
||||
display_name='Log to Console',
|
||||
)
|
||||
# log_console_level: bl_logger.BLExtLogLevel = BLField(
|
||||
# gather.blext_info(blext_pkg).log_console_level,
|
||||
# display_name='Console Log Level',
|
||||
# )
|
||||
|
||||
####################
|
||||
# - Logging
|
||||
####################
|
||||
def conform_logger(self, logger: logging.Logger) -> None:
|
||||
"""Conform a logger's settings to the extension preferences.
|
||||
|
||||
Parameters:
|
||||
logger: The logger to configure using settings in the addon preferences.
|
||||
"""
|
||||
bl_logger.update_logger(
|
||||
logger,
|
||||
cb_console_handler=bl_logger.sensible_console_handler,
|
||||
cb_file_handler=bl_logger.sensible_file_handler,
|
||||
console_level=(
|
||||
self.log_console_level.log_level if self.use_log_console else None
|
||||
),
|
||||
file_path=(self.log_file_path if self.use_log_file else None),
|
||||
file_level=(
|
||||
self.log_file_level.log_level if self.use_log_file else None
|
||||
),
|
||||
)
|
||||
|
||||
# @bl_event.run_on_event(bl_event.Event.BLClassRegistered)
|
||||
# @bl_event.run_on_event(
|
||||
# bl_event.Event.PropChanged,
|
||||
# on_props={
|
||||
# 'use_log_file',
|
||||
# 'log_file_level',
|
||||
# 'log_file_path',
|
||||
# 'use_log_console',
|
||||
# 'log_console_level',
|
||||
# },
|
||||
# )
|
||||
def conform_all_loggers_to_prefs(self) -> None:
|
||||
"""Called to reconfigure all loggers to match newly-altered addon preferences.
|
||||
|
||||
This causes ex. changes to desired console log level to immediately be applied, but only the this addon's loggers.
|
||||
|
||||
Parameters:
|
||||
single_logger_to_setup: When set, only this logger will be setup.
|
||||
Otherwise, **all addon loggers will be setup**.
|
||||
"""
|
||||
blext_pkg = self.bl_idname
|
||||
for logger in bl_logger.all_blext_loggers(blext_pkg):
|
||||
self.setup_logger(logger)
|
||||
|
||||
####################
|
||||
# - UI
|
||||
####################
|
||||
def draw_logging_prefs(self, layout: bpy.types.UILayout) -> None:
|
||||
"""Draw the standardized addon preferences into a UILayout.
|
||||
|
||||
Examples:
|
||||
Run this within the `draw()` method of the addon preferences class like so:
|
||||
```python
|
||||
layout = self.layout
|
||||
self.draw_standard_prefs(layout)
|
||||
```
|
||||
|
||||
Parameters:
|
||||
context: The Blender context object.
|
||||
"""
|
||||
####################
|
||||
# - Logging
|
||||
####################
|
||||
# Box: Log Level
|
||||
box = layout.box()
|
||||
row = box.row()
|
||||
row.alignment = 'CENTER'
|
||||
row.label(text='Logging')
|
||||
|
||||
# Split
|
||||
split = box.split(factor=0.5)
|
||||
|
||||
## Split Col: Console Logging
|
||||
col = split.column()
|
||||
row = col.row()
|
||||
self.draw_blfield(row, 'use_log_console', toggle=True)
|
||||
|
||||
row = col.row()
|
||||
row.enabled = self.use_log_console
|
||||
# self.draw_blfield(row, 'log_level_console')
|
||||
|
||||
## Split Col: File Logging
|
||||
col = split.column()
|
||||
row = col.row()
|
||||
self.draw_blfield(row, 'use_log_file', toggle=True)
|
||||
|
||||
row = col.row()
|
||||
row.enabled = self.use_log_file
|
||||
# self.draw_blfield(row, 'log_file_path')
|
||||
|
||||
row = col.row()
|
||||
row.enabled = self.use_log_file
|
||||
# self.draw_blfield(row, 'log_level_file')
|
||||
|
||||
return _LoggingPrefs
|
||||
|
||||
|
||||
def SensiblePrefs(blext_pkg: str) -> type[type]:
|
||||
class _SensiblePrefs(LoggingPrefs(blext_pkg)):
|
||||
"""Sensible set of baseline preferences fields, for use in extensions.
|
||||
|
||||
Implements the following `blroots` integration:
|
||||
|
||||
- `bl_logger`: Enable the integration of `blroots` logging with extension preferences.
|
||||
"""
|
||||
|
||||
return _SensiblePrefs
|
|
@ -1,4 +1,4 @@
|
|||
# blext
|
||||
# blroots
|
||||
# Copyright (C) 2025 blext Project Contributors
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
|
|
|
@ -23,11 +23,12 @@ import typing as typ
|
|||
from pathlib import Path
|
||||
|
||||
import bpy
|
||||
import pydantic as pyd
|
||||
import rich.console
|
||||
import rich.logging
|
||||
import rich.traceback
|
||||
|
||||
from blroots import gather
|
||||
from blroots import bl_prop, gather
|
||||
|
||||
####################
|
||||
# - Configuration
|
||||
|
@ -53,12 +54,12 @@ LogLevel: typ.TypeAlias = int
|
|||
class BLExtLogLevel(enum.StrEnum):
|
||||
"""Log level of a Blender extension.
|
||||
|
||||
Attributes:
|
||||
Debug: Low-level messages for debugging.
|
||||
Info: Communications about routine operations.
|
||||
Warning: Something that might be wrong.
|
||||
Error: Something wrong that can be recovered from.
|
||||
Critical: Something wrong that cannot be recovered from.
|
||||
Attributes:
|
||||
Debug: Low-level messages for debugging.
|
||||
Info: Communications about routine operations.
|
||||
Warning: Something that might be wrong.
|
||||
Error: Something wrong that can be recovered from.
|
||||
Critical: Something wrong that cannot be recovered from.
|
||||
"""
|
||||
|
||||
Debug = enum.auto()
|
||||
|
@ -292,3 +293,165 @@ def sensible_file_handler(
|
|||
file_handler.setFormatter(file_formatter)
|
||||
file_handler.setLevel(level)
|
||||
return file_handler
|
||||
|
||||
|
||||
####################
|
||||
# - Logging Preferences
|
||||
####################
|
||||
class BL_LoggingPrefs(bpy.types.PropertyGroup):
|
||||
bl_props = frozenset(
|
||||
{
|
||||
'use_log_file',
|
||||
'log_file_level',
|
||||
'log_file_path',
|
||||
'use_log_console',
|
||||
'log_console_level',
|
||||
}
|
||||
)
|
||||
|
||||
# File Logging
|
||||
use_log_file: bool = BLField(
|
||||
gather.blext_info(blext_pkg).use_log_file,
|
||||
display_name='Log to File',
|
||||
)
|
||||
log_file_level: BLExtLogLevel = BLField(
|
||||
gather.blext_info(blext_pkg).log_file_level,
|
||||
display_name='File Log Level',
|
||||
)
|
||||
log_file_path: Path = BLField(
|
||||
gather.blext_info(blext_pkg).log_file_path,
|
||||
display_name='File Log Path',
|
||||
)
|
||||
|
||||
# Console Logging
|
||||
use_log_console: bool = BLField(
|
||||
gather.blext_info(blext_pkg).use_log_console,
|
||||
display_name='Log to Console',
|
||||
is_file=True,
|
||||
)
|
||||
log_console_level: BLExtLogLevel = BLField(
|
||||
gather.blext_info(blext_pkg).log_console_level,
|
||||
display_name='Console Log Level',
|
||||
)
|
||||
|
||||
|
||||
class LoggingPrefs(pyd.BaseModel, frozen=True):
|
||||
@classmethod
|
||||
def bl_prop_group(cls) -> type[bpy.types.PropertyGroup]:
|
||||
return BL_LoggingPrefs
|
||||
|
||||
bl_idname: str
|
||||
|
||||
# Console Logging
|
||||
use_log_console: bool = gather.blext_info(blext_pkg).use_log_console
|
||||
log_console_level: BLExtLogLevel = gather.blext_info(blext_pkg).log_console_level
|
||||
|
||||
# File Logging
|
||||
use_log_file: bool = gather.blext_info(blext_pkg).use_log_file
|
||||
log_file_level: BLExtLogLevel = gather.blext_info(blext_pkg).log_file_level
|
||||
log_file_path: Path = gather.blext_info(blext_pkg).log_file_path
|
||||
|
||||
# Console Logging
|
||||
use_log_console: bool = gather.blext_info(blext_pkg).use_log_console
|
||||
log_console_level: BLExtLogLevel = gather.blext_info(blext_pkg).log_console_level
|
||||
|
||||
####################
|
||||
# - Logging
|
||||
####################
|
||||
def conform_logger(self, logger: logging.Logger) -> None:
|
||||
"""Conform a logger's settings to the extension preferences.
|
||||
|
||||
Parameters:
|
||||
logger: The logger to configure using settings in the addon preferences.
|
||||
"""
|
||||
update_logger(
|
||||
logger,
|
||||
cb_console_handler=sensible_console_handler,
|
||||
cb_file_handler=sensible_file_handler,
|
||||
console_level=(
|
||||
self.log_console_level.log_level if self.use_log_console else None
|
||||
),
|
||||
file_path=(self.log_file_path if self.use_log_file else None),
|
||||
file_level=(self.log_file_level.log_level if self.use_log_file else None),
|
||||
)
|
||||
|
||||
@bl_event.run_on_event(bl_event.Event.BLClassRegistered)
|
||||
@bl_event.run_on_event(
|
||||
bl_event.Event.PropChanged,
|
||||
on_props={
|
||||
'use_log_file',
|
||||
'log_file_level',
|
||||
'log_file_path',
|
||||
'use_log_console',
|
||||
'log_console_level',
|
||||
},
|
||||
)
|
||||
@bl_event.propagate(source='parent', props={'bl_idname': 'blext_pkg'})
|
||||
def conform_all_loggers_to_prefs(self, blext_pkg: str) -> None:
|
||||
## TODO: Do some kind of event magic to get the blext_pkg in here frombl_idname of the parent bpy.types.AddonPreferences object containing this class.
|
||||
"""Called to reconfigure all loggers to match newly-altered addon preferences.
|
||||
|
||||
This causes ex. changes to desired console log level to immediately be applied, but only the this addon's loggers.
|
||||
|
||||
Parameters:
|
||||
single_logger_to_setup: When set, only this logger will be setup.
|
||||
Otherwise, **all addon loggers will be setup**.
|
||||
"""
|
||||
# Retrieve extension __package__ from bl_idname of bpy.types.AddonPreferences
|
||||
for logger in all_blext_loggers(blext_pkg):
|
||||
self.conform_logger(logger)
|
||||
|
||||
####################
|
||||
# - UI
|
||||
####################
|
||||
def draw(
|
||||
self,
|
||||
bl_instance: bl_instance.BLInstance,
|
||||
layout: bpy.types.UILayout,
|
||||
_: bpy.types.Context,
|
||||
) -> None:
|
||||
"""Draw the standardized addon preferences into a UILayout.
|
||||
|
||||
Examples:
|
||||
Run this within the `draw()` method of the addon preferences class like so:
|
||||
```python
|
||||
layout = self.layout
|
||||
self.draw_standard_prefs(layout)
|
||||
```
|
||||
|
||||
Parameters:
|
||||
context: The Blender context object.
|
||||
"""
|
||||
####################
|
||||
# - Logging
|
||||
####################
|
||||
# Box: Log Level
|
||||
box = layout.box()
|
||||
row = box.row()
|
||||
row.alignment = 'CENTER'
|
||||
row.label(text='Logging')
|
||||
|
||||
# Split
|
||||
split = box.split(factor=0.5)
|
||||
|
||||
## Split Col: Console Logging
|
||||
col = split.column()
|
||||
row = col.row()
|
||||
bl_instance.draw_blprop(row, 'use_log_console', toggle=True)
|
||||
|
||||
row = col.row()
|
||||
row.enabled = self.use_log_console
|
||||
bl_instance.draw_blprop(row, 'log_console_level', text='Level')
|
||||
|
||||
## Split Col: File Logging
|
||||
col = split.column()
|
||||
row = col.row()
|
||||
bl_instance.draw_blprop(row, 'use_log_file', toggle=True)
|
||||
|
||||
row = col.row()
|
||||
row.enabled = self.use_log_file
|
||||
bl_instance.draw_blprop(row, 'log_file_level', text='Level')
|
||||
|
||||
row = col.row()
|
||||
row.enabled = self.use_log_file
|
||||
bl_instance.draw_blprop(row, 'log_file_path', text='')
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
# blext
|
||||
# Copyright (C) 2025 blext Project Contributors
|
||||
# blroots
|
||||
# Copyright (C) 2025 blroots Project Contributors
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU Affero General Public License as published by
|
||||
|
@ -13,9 +13,6 @@
|
|||
#
|
||||
# You should have received a copy of the GNU Affero General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
"""Provides identifiers for Blender operators defined by this addon."""
|
||||
|
||||
import enum
|
||||
|
||||
import blroots as blr
|
||||
|
@ -23,7 +20,17 @@ import blroots as blr
|
|||
BLEXT_INFO = blr.blext_info(__name__)
|
||||
|
||||
|
||||
class Icon(enum.StrEnum):
|
||||
"""Identifiers for icons used throughout this addon."""
|
||||
|
||||
|
||||
class OperatorType(enum.StrEnum):
|
||||
"""Identifiers for addon-defined `bpy.types.Operator`."""
|
||||
|
||||
SimpleOperator = BLEXT_INFO.operator_name('simple_operator')
|
||||
|
||||
|
||||
class PanelType(enum.StrEnum):
|
||||
"""Identifiers for addon-defined `bpy.types.Panel`."""
|
||||
|
||||
SimplePanel = BLEXT_INFO.panel_name('simple_panel')
|
|
@ -1,31 +0,0 @@
|
|||
# blext
|
||||
# Copyright (C) 2025 blext Project Contributors
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU Affero General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU Affero General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU Affero General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
"""Independent constants and types, which represent a kind of 'social contract' governing communication between all components of the addon."""
|
||||
|
||||
from .icons import Icon
|
||||
from .operator_types import (
|
||||
OperatorType,
|
||||
)
|
||||
from .panel_types import (
|
||||
PanelType,
|
||||
)
|
||||
|
||||
__all__ = [
|
||||
'Icon',
|
||||
'OperatorType',
|
||||
'PanelType',
|
||||
]
|
|
@ -1,23 +0,0 @@
|
|||
# blext
|
||||
# Copyright (C) 2025 blext Project Contributors
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU Affero General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU Affero General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU Affero General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
"""Provides an enum that semantically constrains the use of icons throughout the addon."""
|
||||
|
||||
import enum
|
||||
|
||||
|
||||
class Icon(enum.StrEnum):
|
||||
"""Identifiers for icons used throughout this addon."""
|
|
@ -1,29 +0,0 @@
|
|||
# blext
|
||||
# Copyright (C) 2025 blext Project Contributors
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU Affero General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU Affero General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU Affero General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
"""Provides identifiers for Blender panels defined by this addon."""
|
||||
|
||||
import enum
|
||||
|
||||
import blroots as blr
|
||||
|
||||
BLEXT_INFO = blr.blext_info(__name__)
|
||||
|
||||
|
||||
class PanelType(enum.StrEnum):
|
||||
"""Identifiers for addon-defined `bpy.types.Panel`."""
|
||||
|
||||
SimplePanel = BLEXT_INFO.panel_name('simple_panel')
|
|
@ -40,10 +40,9 @@ class SimpleOperator(bpy.types.Operator):
|
|||
|
||||
def execute(self, context: bpy.types.Context) -> blr.typ.BLOperatorStatus:
|
||||
"""Display a simple message on execution."""
|
||||
scene = context.scene
|
||||
self.report(
|
||||
{'INFO'},
|
||||
f'The simple integer was set to {scene.simple_integer}.',
|
||||
f'The simple integer was set to {context.scene.simple_integer}.',
|
||||
)
|
||||
|
||||
return {'FINISHED'}
|
||||
|
@ -53,5 +52,3 @@ class SimpleOperator(bpy.types.Operator):
|
|||
# - Blender Registration
|
||||
####################
|
||||
BL_REGISTER = [SimpleOperator]
|
||||
BL_HANDLERS = None
|
||||
BL_KEYMAP_ITEMS = None
|
||||
|
|
|
@ -52,7 +52,7 @@ class SimplePanel(bpy.types.Panel):
|
|||
layout = self.layout
|
||||
|
||||
# Property
|
||||
scene.draw_blfield(layout, 'simple_integer')
|
||||
scene.draw_blprop(layout, 'simple_integer')
|
||||
|
||||
# Operator
|
||||
layout.label(text='Look ma, so many hands!')
|
||||
|
@ -63,5 +63,3 @@ class SimplePanel(bpy.types.Panel):
|
|||
# - Blender Registration
|
||||
####################
|
||||
BL_REGISTER = [SimplePanel]
|
||||
BL_HANDLERS = None
|
||||
BL_KEYMAP_ITEMS = None
|
||||
|
|
|
@ -20,7 +20,7 @@ import blroots as blr
|
|||
import bpy
|
||||
|
||||
|
||||
class BLExtPrefs(blr.prefs.SensiblePrefs(__package__)):
|
||||
class BLExtPrefs(bpy.types.AddonPreferences):
|
||||
"""Manages user preferences and settings.
|
||||
|
||||
Attributes:
|
||||
|
@ -33,6 +33,16 @@ class BLExtPrefs(blr.prefs.SensiblePrefs(__package__)):
|
|||
|
||||
bl_idname = __package__
|
||||
|
||||
# Logging
|
||||
logging_prefs: blr.utils.bl_logger.LoggingPrefs = blr.BLField(
|
||||
blr.gather.blext_info.logging_prefs,
|
||||
display_name='Logging Settings',
|
||||
)
|
||||
## Methods on this class should include:
|
||||
## - conform_logger
|
||||
## - conform_all_loggers_to_prefs
|
||||
## - draw: Draw these prefs into a layout.
|
||||
|
||||
####################
|
||||
# - UI
|
||||
####################
|
||||
|
@ -46,8 +56,7 @@ class BLExtPrefs(blr.prefs.SensiblePrefs(__package__)):
|
|||
context: The Blender context object.
|
||||
"""
|
||||
layout = self.layout
|
||||
|
||||
self.draw_logging_prefs(layout)
|
||||
self.draw_blprop(layout, 'logging_prefs')
|
||||
|
||||
|
||||
####################
|
||||
|
|
|
@ -146,7 +146,32 @@ dev = [
|
|||
]
|
||||
|
||||
[[package]]
|
||||
name = "blext-simple-example"
|
||||
name = "blroots"
|
||||
version = "0.1.0"
|
||||
source = { editable = "../../" }
|
||||
dependencies = [
|
||||
{ name = "griffe" },
|
||||
{ name = "pydantic" },
|
||||
{ name = "rich" },
|
||||
]
|
||||
|
||||
[package.metadata]
|
||||
requires-dist = [
|
||||
{ name = "griffe", specifier = ">=1.5.5" },
|
||||
{ name = "pydantic", specifier = ">=2.10.5" },
|
||||
{ name = "rich", specifier = ">=13.9.4" },
|
||||
]
|
||||
|
||||
[package.metadata.requires-dev]
|
||||
dev = [
|
||||
{ name = "commitizen", specifier = ">=4.1.0" },
|
||||
{ name = "mypy", specifier = ">=1.14.1" },
|
||||
{ name = "pytest", specifier = ">=8.3.4" },
|
||||
{ name = "ruff", specifier = ">=0.9.1" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "blroots-simple-example"
|
||||
version = "0.1.0"
|
||||
source = { virtual = "." }
|
||||
dependencies = [
|
||||
|
@ -178,31 +203,6 @@ dev = [
|
|||
{ name = "typer", specifier = ">=0.15.1" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "blroots"
|
||||
version = "0.1.0"
|
||||
source = { editable = "../../" }
|
||||
dependencies = [
|
||||
{ name = "griffe" },
|
||||
{ name = "pydantic" },
|
||||
{ name = "rich" },
|
||||
]
|
||||
|
||||
[package.metadata]
|
||||
requires-dist = [
|
||||
{ name = "griffe", specifier = ">=1.5.5" },
|
||||
{ name = "pydantic", specifier = ">=2.10.5" },
|
||||
{ name = "rich", specifier = ">=13.9.4" },
|
||||
]
|
||||
|
||||
[package.metadata.requires-dev]
|
||||
dev = [
|
||||
{ name = "commitizen", specifier = ">=4.1.0" },
|
||||
{ name = "mypy", specifier = ">=1.14.1" },
|
||||
{ name = "pytest", specifier = ">=8.3.4" },
|
||||
{ name = "ruff", specifier = ">=0.9.1" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "click"
|
||||
version = "8.1.8"
|
||||
|
|
|
@ -33,6 +33,7 @@ Homepage = "https://codeberg.org/so-rose/blroots"
|
|||
|
||||
[dependency-groups]
|
||||
dev = [
|
||||
"bpy~=4.3.0",
|
||||
"commitizen>=4.1.0",
|
||||
"mypy>=1.14.1",
|
||||
"pytest>=8.3.4",
|
||||
|
@ -155,33 +156,23 @@ quote-style = "single"
|
|||
indent-style = "tab"
|
||||
docstring-code-format = false
|
||||
|
||||
|
||||
####################
|
||||
# - Tooling: MyPy
|
||||
# - Tool: basedpyright
|
||||
####################
|
||||
[tool.mypy]
|
||||
python_version = '3.11'
|
||||
python_executable="./.venv/bin/python"
|
||||
[tool.basedpyright]
|
||||
defineConstant = { DEBUG = true }
|
||||
|
||||
warn_redundant_casts = true
|
||||
warn_unused_ignores = true
|
||||
warn_return_any = true
|
||||
#include = ["blext"]
|
||||
#strict = ["blext"]
|
||||
|
||||
strict_optional = true
|
||||
no_implicit_optional = true
|
||||
reportMissingImports = "error"
|
||||
reportMissingTypeStubs = true
|
||||
|
||||
disallow_subclassing_any = false
|
||||
disallow_any_generics = true
|
||||
disallow_untyped_calls = true
|
||||
disallow_incomplete_defs = true
|
||||
|
||||
check_untyped_defs = true
|
||||
disallow_untyped_decorators = true
|
||||
|
||||
ignore_missing_imports = true
|
||||
|
||||
plugins = [
|
||||
# 'pydantic.mypy',
|
||||
# 'typing_protocol_intersection.mypy_plugin',
|
||||
executionEnvironments = [
|
||||
{ root = "blroots", pythonVersion = "3.11", extraPaths = [ ".venv/lib/python3.11/site-packages", ".venv/lib/python3.11/site-packages/bpy/4.3/scripts/modules" ] },
|
||||
#{ root = ".", pythonVersion = "3.11", extraPaths = [ ".venv/lib/python3.11/site-packages" ] },
|
||||
{ root = "examples/simple", pythonVersion = "3.11", extraPaths = [ ".venv/lib/python3.11/site-packages", ".venv/lib/python3.11/site-packages/bpy/4.3/scripts/modules" ] },
|
||||
]
|
||||
|
||||
|
||||
|
|
255
uv.lock
255
uv.lock
|
@ -31,6 +31,7 @@ dependencies = [
|
|||
|
||||
[package.dev-dependencies]
|
||||
dev = [
|
||||
{ name = "bpy" },
|
||||
{ name = "commitizen" },
|
||||
{ name = "mypy" },
|
||||
{ name = "pytest" },
|
||||
|
@ -46,12 +47,84 @@ requires-dist = [
|
|||
|
||||
[package.metadata.requires-dev]
|
||||
dev = [
|
||||
{ name = "bpy", specifier = ">=4.3.0" },
|
||||
{ name = "commitizen", specifier = ">=4.1.0" },
|
||||
{ name = "mypy", specifier = ">=1.14.1" },
|
||||
{ name = "pytest", specifier = ">=8.3.4" },
|
||||
{ name = "ruff", specifier = ">=0.9.1" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "bpy"
|
||||
version = "4.3.0"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
dependencies = [
|
||||
{ name = "cython" },
|
||||
{ name = "numpy" },
|
||||
{ name = "requests" },
|
||||
{ name = "zstandard" },
|
||||
]
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/c1/fc/5902fa508a13f4f1669bc2e2cd8f37adf1caeeed0a344f627c0ffc3bdc40/bpy-4.3.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:82c3486e9762e8cfce4bbc1d6752f5625da8515f6b217e02810350627d693897", size = 217245993 },
|
||||
{ url = "https://files.pythonhosted.org/packages/81/39/4e763b150ff05412ab25d5eae91da7f0ade474b5d66509b39a67066d35ec/bpy-4.3.0-cp311-cp311-macosx_11_0_x86_64.whl", hash = "sha256:490aa94699664a99eee755047485a3ad70470fc6267c55a73459660f52299ade", size = 230465428 },
|
||||
{ url = "https://files.pythonhosted.org/packages/9c/4d/897bf3c247d7c6a4e75397f4c5dd5d201fb147981d2271ac35ac955959ab/bpy-4.3.0-cp311-cp311-manylinux_2_28_x86_64.whl", hash = "sha256:1620199392a7e7aab58672aa47c50b3c5b1e4d39d46af5a7f9bbe2af561ea942", size = 377434308 },
|
||||
{ url = "https://files.pythonhosted.org/packages/7f/da/c255b626cab58c69c49f4a65d0410b36aa1086dc63862d1c3de653f0a5a0/bpy-4.3.0-cp311-cp311-win_amd64.whl", hash = "sha256:1a7b2c516aa7a95c31fcaae8842addd07fa86c1ce4768e88a34b480d17e2e93a", size = 333159335 },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "certifi"
|
||||
version = "2025.1.31"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/1c/ab/c9f1e32b7b1bf505bf26f0ef697775960db7932abeb7b516de930ba2705f/certifi-2025.1.31.tar.gz", hash = "sha256:3d5da6925056f6f18f119200434a4780a94263f10d1c21d032a6f6b2baa20651", size = 167577 }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/38/fc/bce832fd4fd99766c04d1ee0eead6b0ec6486fb100ae5e74c1d91292b982/certifi-2025.1.31-py3-none-any.whl", hash = "sha256:ca78db4565a652026a4db2bcdf68f2fb589ea80d0be70e03929ed730746b84fe", size = 166393 },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cffi"
|
||||
version = "1.17.1"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
dependencies = [
|
||||
{ name = "pycparser" },
|
||||
]
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/fc/97/c783634659c2920c3fc70419e3af40972dbaf758daa229a7d6ea6135c90d/cffi-1.17.1.tar.gz", hash = "sha256:1c39c6016c32bc48dd54561950ebd6836e1670f2ae46128f67cf49e789c52824", size = 516621 }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/6b/f4/927e3a8899e52a27fa57a48607ff7dc91a9ebe97399b357b85a0c7892e00/cffi-1.17.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:a45e3c6913c5b87b3ff120dcdc03f6131fa0065027d0ed7ee6190736a74cd401", size = 182264 },
|
||||
{ url = "https://files.pythonhosted.org/packages/6c/f5/6c3a8efe5f503175aaddcbea6ad0d2c96dad6f5abb205750d1b3df44ef29/cffi-1.17.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:30c5e0cb5ae493c04c8b42916e52ca38079f1b235c2f8ae5f4527b963c401caf", size = 178651 },
|
||||
{ url = "https://files.pythonhosted.org/packages/94/dd/a3f0118e688d1b1a57553da23b16bdade96d2f9bcda4d32e7d2838047ff7/cffi-1.17.1-cp311-cp311-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f75c7ab1f9e4aca5414ed4d8e5c0e303a34f4421f8a0d47a4d019ceff0ab6af4", size = 445259 },
|
||||
{ url = "https://files.pythonhosted.org/packages/2e/ea/70ce63780f096e16ce8588efe039d3c4f91deb1dc01e9c73a287939c79a6/cffi-1.17.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a1ed2dd2972641495a3ec98445e09766f077aee98a1c896dcb4ad0d303628e41", size = 469200 },
|
||||
{ url = "https://files.pythonhosted.org/packages/1c/a0/a4fa9f4f781bda074c3ddd57a572b060fa0df7655d2a4247bbe277200146/cffi-1.17.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:46bf43160c1a35f7ec506d254e5c890f3c03648a4dbac12d624e4490a7046cd1", size = 477235 },
|
||||
{ url = "https://files.pythonhosted.org/packages/62/12/ce8710b5b8affbcdd5c6e367217c242524ad17a02fe5beec3ee339f69f85/cffi-1.17.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a24ed04c8ffd54b0729c07cee15a81d964e6fee0e3d4d342a27b020d22959dc6", size = 459721 },
|
||||
{ url = "https://files.pythonhosted.org/packages/ff/6b/d45873c5e0242196f042d555526f92aa9e0c32355a1be1ff8c27f077fd37/cffi-1.17.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:610faea79c43e44c71e1ec53a554553fa22321b65fae24889706c0a84d4ad86d", size = 467242 },
|
||||
{ url = "https://files.pythonhosted.org/packages/1a/52/d9a0e523a572fbccf2955f5abe883cfa8bcc570d7faeee06336fbd50c9fc/cffi-1.17.1-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:a9b15d491f3ad5d692e11f6b71f7857e7835eb677955c00cc0aefcd0669adaf6", size = 477999 },
|
||||
{ url = "https://files.pythonhosted.org/packages/44/74/f2a2460684a1a2d00ca799ad880d54652841a780c4c97b87754f660c7603/cffi-1.17.1-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:de2ea4b5833625383e464549fec1bc395c1bdeeb5f25c4a3a82b5a8c756ec22f", size = 454242 },
|
||||
{ url = "https://files.pythonhosted.org/packages/f8/4a/34599cac7dfcd888ff54e801afe06a19c17787dfd94495ab0c8d35fe99fb/cffi-1.17.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:fc48c783f9c87e60831201f2cce7f3b2e4846bf4d8728eabe54d60700b318a0b", size = 478604 },
|
||||
{ url = "https://files.pythonhosted.org/packages/34/33/e1b8a1ba29025adbdcda5fb3a36f94c03d771c1b7b12f726ff7fef2ebe36/cffi-1.17.1-cp311-cp311-win32.whl", hash = "sha256:85a950a4ac9c359340d5963966e3e0a94a676bd6245a4b55bc43949eee26a655", size = 171727 },
|
||||
{ url = "https://files.pythonhosted.org/packages/3d/97/50228be003bb2802627d28ec0627837ac0bf35c90cf769812056f235b2d1/cffi-1.17.1-cp311-cp311-win_amd64.whl", hash = "sha256:caaf0640ef5f5517f49bc275eca1406b0ffa6aa184892812030f04c2abf589a0", size = 181400 },
|
||||
{ url = "https://files.pythonhosted.org/packages/5a/84/e94227139ee5fb4d600a7a4927f322e1d4aea6fdc50bd3fca8493caba23f/cffi-1.17.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:805b4371bf7197c329fcb3ead37e710d1bca9da5d583f5073b799d5c5bd1eee4", size = 183178 },
|
||||
{ url = "https://files.pythonhosted.org/packages/da/ee/fb72c2b48656111c4ef27f0f91da355e130a923473bf5ee75c5643d00cca/cffi-1.17.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:733e99bc2df47476e3848417c5a4540522f234dfd4ef3ab7fafdf555b082ec0c", size = 178840 },
|
||||
{ url = "https://files.pythonhosted.org/packages/cc/b6/db007700f67d151abadf508cbfd6a1884f57eab90b1bb985c4c8c02b0f28/cffi-1.17.1-cp312-cp312-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1257bdabf294dceb59f5e70c64a3e2f462c30c7ad68092d01bbbfb1c16b1ba36", size = 454803 },
|
||||
{ url = "https://files.pythonhosted.org/packages/1a/df/f8d151540d8c200eb1c6fba8cd0dfd40904f1b0682ea705c36e6c2e97ab3/cffi-1.17.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:da95af8214998d77a98cc14e3a3bd00aa191526343078b530ceb0bd710fb48a5", size = 478850 },
|
||||
{ url = "https://files.pythonhosted.org/packages/28/c0/b31116332a547fd2677ae5b78a2ef662dfc8023d67f41b2a83f7c2aa78b1/cffi-1.17.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d63afe322132c194cf832bfec0dc69a99fb9bb6bbd550f161a49e9e855cc78ff", size = 485729 },
|
||||
{ url = "https://files.pythonhosted.org/packages/91/2b/9a1ddfa5c7f13cab007a2c9cc295b70fbbda7cb10a286aa6810338e60ea1/cffi-1.17.1-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f79fc4fc25f1c8698ff97788206bb3c2598949bfe0fef03d299eb1b5356ada99", size = 471256 },
|
||||
{ url = "https://files.pythonhosted.org/packages/b2/d5/da47df7004cb17e4955df6a43d14b3b4ae77737dff8bf7f8f333196717bf/cffi-1.17.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b62ce867176a75d03a665bad002af8e6d54644fad99a3c70905c543130e39d93", size = 479424 },
|
||||
{ url = "https://files.pythonhosted.org/packages/0b/ac/2a28bcf513e93a219c8a4e8e125534f4f6db03e3179ba1c45e949b76212c/cffi-1.17.1-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:386c8bf53c502fff58903061338ce4f4950cbdcb23e2902d86c0f722b786bbe3", size = 484568 },
|
||||
{ url = "https://files.pythonhosted.org/packages/d4/38/ca8a4f639065f14ae0f1d9751e70447a261f1a30fa7547a828ae08142465/cffi-1.17.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:4ceb10419a9adf4460ea14cfd6bc43d08701f0835e979bf821052f1805850fe8", size = 488736 },
|
||||
{ url = "https://files.pythonhosted.org/packages/86/c5/28b2d6f799ec0bdecf44dced2ec5ed43e0eb63097b0f58c293583b406582/cffi-1.17.1-cp312-cp312-win32.whl", hash = "sha256:a08d7e755f8ed21095a310a693525137cfe756ce62d066e53f502a83dc550f65", size = 172448 },
|
||||
{ url = "https://files.pythonhosted.org/packages/50/b9/db34c4755a7bd1cb2d1603ac3863f22bcecbd1ba29e5ee841a4bc510b294/cffi-1.17.1-cp312-cp312-win_amd64.whl", hash = "sha256:51392eae71afec0d0c8fb1a53b204dbb3bcabcb3c9b807eedf3e1e6ccf2de903", size = 181976 },
|
||||
{ url = "https://files.pythonhosted.org/packages/8d/f8/dd6c246b148639254dad4d6803eb6a54e8c85c6e11ec9df2cffa87571dbe/cffi-1.17.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:f3a2b4222ce6b60e2e8b337bb9596923045681d71e5a082783484d845390938e", size = 182989 },
|
||||
{ url = "https://files.pythonhosted.org/packages/8b/f1/672d303ddf17c24fc83afd712316fda78dc6fce1cd53011b839483e1ecc8/cffi-1.17.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:0984a4925a435b1da406122d4d7968dd861c1385afe3b45ba82b750f229811e2", size = 178802 },
|
||||
{ url = "https://files.pythonhosted.org/packages/0e/2d/eab2e858a91fdff70533cab61dcff4a1f55ec60425832ddfdc9cd36bc8af/cffi-1.17.1-cp313-cp313-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d01b12eeeb4427d3110de311e1774046ad344f5b1a7403101878976ecd7a10f3", size = 454792 },
|
||||
{ url = "https://files.pythonhosted.org/packages/75/b2/fbaec7c4455c604e29388d55599b99ebcc250a60050610fadde58932b7ee/cffi-1.17.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:706510fe141c86a69c8ddc029c7910003a17353970cff3b904ff0686a5927683", size = 478893 },
|
||||
{ url = "https://files.pythonhosted.org/packages/4f/b7/6e4a2162178bf1935c336d4da8a9352cccab4d3a5d7914065490f08c0690/cffi-1.17.1-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:de55b766c7aa2e2a3092c51e0483d700341182f08e67c63630d5b6f200bb28e5", size = 485810 },
|
||||
{ url = "https://files.pythonhosted.org/packages/c7/8a/1d0e4a9c26e54746dc08c2c6c037889124d4f59dffd853a659fa545f1b40/cffi-1.17.1-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c59d6e989d07460165cc5ad3c61f9fd8f1b4796eacbd81cee78957842b834af4", size = 471200 },
|
||||
{ url = "https://files.pythonhosted.org/packages/26/9f/1aab65a6c0db35f43c4d1b4f580e8df53914310afc10ae0397d29d697af4/cffi-1.17.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dd398dbc6773384a17fe0d3e7eeb8d1a21c2200473ee6806bb5e6a8e62bb73dd", size = 479447 },
|
||||
{ url = "https://files.pythonhosted.org/packages/5f/e4/fb8b3dd8dc0e98edf1135ff067ae070bb32ef9d509d6cb0f538cd6f7483f/cffi-1.17.1-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:3edc8d958eb099c634dace3c7e16560ae474aa3803a5df240542b305d14e14ed", size = 484358 },
|
||||
{ url = "https://files.pythonhosted.org/packages/f1/47/d7145bf2dc04684935d57d67dff9d6d795b2ba2796806bb109864be3a151/cffi-1.17.1-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:72e72408cad3d5419375fc87d289076ee319835bdfa2caad331e377589aebba9", size = 488469 },
|
||||
{ url = "https://files.pythonhosted.org/packages/bf/ee/f94057fa6426481d663b88637a9a10e859e492c73d0384514a17d78ee205/cffi-1.17.1-cp313-cp313-win32.whl", hash = "sha256:e03eab0a8677fa80d646b5ddece1cbeaf556c313dcfac435ba11f107ba117b5d", size = 172475 },
|
||||
{ url = "https://files.pythonhosted.org/packages/7c/fc/6a8cb64e5f0324877d503c854da15d76c1e50eb722e320b15345c4d0c6de/cffi-1.17.1-cp313-cp313-win_amd64.whl", hash = "sha256:f6a16c31041f09ead72d69f583767292f750d24913dadacf5756b966aacb3f1a", size = 182009 },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "charset-normalizer"
|
||||
version = "3.4.1"
|
||||
|
@ -130,6 +203,39 @@ wheels = [
|
|||
{ url = "https://files.pythonhosted.org/packages/48/f7/7f70adfbf3553ffdbe391eaacde72b21dbc1b4226ae56ca32e8ded1bf70b/commitizen-4.1.0-py3-none-any.whl", hash = "sha256:2e6c5fbd442cab4bcc5a04bc86ef2196ef84bcf611317d6c596e87f5bb4c09f5", size = 72282 },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cython"
|
||||
version = "3.0.12"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/5a/25/886e197c97a4b8e254173002cdc141441e878ff29aaa7d9ba560cd6e4866/cython-3.0.12.tar.gz", hash = "sha256:b988bb297ce76c671e28c97d017b95411010f7c77fa6623dd0bb47eed1aee1bc", size = 2757617 }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/7e/60/3d27abd940f7b80a6aeb69dc093a892f04828e1dd0b243dd81ff87d7b0e9/Cython-3.0.12-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:feb86122a823937cc06e4c029d80ff69f082ebb0b959ab52a5af6cdd271c5dc3", size = 3277430 },
|
||||
{ url = "https://files.pythonhosted.org/packages/c7/49/f17b0541b317d11f1d021a580643ee2481685157cded92efb32e2fb4daef/Cython-3.0.12-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:dfdbea486e702c328338314adb8e80f5f9741f06a0ae83aaec7463bc166d12e8", size = 3444055 },
|
||||
{ url = "https://files.pythonhosted.org/packages/6b/7f/c57791ba6a1c934b6f1ab51371e894e3b4bfde0bc35e50046c8754a9d215/Cython-3.0.12-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:563de1728c8e48869d2380a1b76bbc1b1b1d01aba948480d68c1d05e52d20c92", size = 3597874 },
|
||||
{ url = "https://files.pythonhosted.org/packages/23/24/803a0db3681b3a2ef65a4bebab201e5ae4aef5e6127ae03683476a573aa9/Cython-3.0.12-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:398d4576c1e1f6316282aa0b4a55139254fbed965cba7813e6d9900d3092b128", size = 3644129 },
|
||||
{ url = "https://files.pythonhosted.org/packages/27/13/9b53ba8336e083ece441af8d6d182b8ca83ad523e87c07b3190af379ebc3/Cython-3.0.12-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:1e5eadef80143026944ea8f9904715a008f5108d1d644a89f63094cc37351e73", size = 3504936 },
|
||||
{ url = "https://files.pythonhosted.org/packages/a9/d2/d11104be6992a9fe256860cae6d1a79f7dcf3bdb12ae00116fac591f677d/Cython-3.0.12-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:5a93cbda00a5451175b97dea5a9440a3fcee9e54b4cba7a7dbcba9a764b22aec", size = 3713066 },
|
||||
{ url = "https://files.pythonhosted.org/packages/d9/8c/1fe49135296efa3f460c760a4297f6a5b387f3e69ac5c9dcdbd620295ab3/Cython-3.0.12-cp311-cp311-win32.whl", hash = "sha256:3109e1d44425a2639e9a677b66cd7711721a5b606b65867cb2d8ef7a97e2237b", size = 2579935 },
|
||||
{ url = "https://files.pythonhosted.org/packages/02/4e/5ac0b5b9a239cd3fdae187dda8ff06b0b812f671e2501bf253712278f0ac/Cython-3.0.12-cp311-cp311-win_amd64.whl", hash = "sha256:d4b70fc339adba1e2111b074ee6119fe9fd6072c957d8597bce9a0dd1c3c6784", size = 2787337 },
|
||||
{ url = "https://files.pythonhosted.org/packages/e6/6c/3be501a6520a93449b1e7e6f63e598ec56f3b5d1bc7ad14167c72a22ddf7/Cython-3.0.12-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:fe030d4a00afb2844f5f70896b7f2a1a0d7da09bf3aa3d884cbe5f73fff5d310", size = 3311717 },
|
||||
{ url = "https://files.pythonhosted.org/packages/ee/ab/adfeb22c85491de18ae10932165edd5b6f01e4c5e3e363638759d1235015/Cython-3.0.12-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a7fec4f052b8fe173fe70eae75091389955b9a23d5cec3d576d21c5913b49d47", size = 3344337 },
|
||||
{ url = "https://files.pythonhosted.org/packages/0d/72/743730d7c46b4c85abefb93187cbbcb7aae8de288d7722b990db3d13499e/Cython-3.0.12-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0faa5e39e5c8cdf6f9c3b1c3f24972826e45911e7f5b99cf99453fca5432f45e", size = 3517692 },
|
||||
{ url = "https://files.pythonhosted.org/packages/09/a1/29a4759a02661f8c8e6b703f62bfbc8285337e6918cc90f55dc0fadb5eb3/Cython-3.0.12-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2d53de996ed340e9ab0fc85a88aaa8932f2591a2746e1ab1c06e262bd4ec4be7", size = 3577057 },
|
||||
{ url = "https://files.pythonhosted.org/packages/d6/f8/03d74e98901a7cc2f21f95231b07dd54ec2f69477319bac268b3816fc3a8/Cython-3.0.12-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:ea3a0e19ab77266c738aa110684a753a04da4e709472cadeff487133354d6ab8", size = 3396493 },
|
||||
{ url = "https://files.pythonhosted.org/packages/50/ea/ac33c5f54f980dbc23dd8f1d5c51afeef26e15ac1a66388e4b8195af83b7/Cython-3.0.12-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:c151082884be468f2f405645858a857298ac7f7592729e5b54788b5c572717ba", size = 3603859 },
|
||||
{ url = "https://files.pythonhosted.org/packages/a2/4e/91fc1d6b5e678dcf2d1ecd8dce45b014b4b60d2044d376355c605831c873/Cython-3.0.12-cp312-cp312-win32.whl", hash = "sha256:3083465749911ac3b2ce001b6bf17f404ac9dd35d8b08469d19dc7e717f5877a", size = 2610428 },
|
||||
{ url = "https://files.pythonhosted.org/packages/ff/c3/a7fdec227b9f0bb07edbeb016c7b18ed6a8e6ce884d08b2e397cda2c0168/Cython-3.0.12-cp312-cp312-win_amd64.whl", hash = "sha256:c0b91c7ebace030dd558ea28730de8c580680b50768e5af66db2904a3716c3e3", size = 2794755 },
|
||||
{ url = "https://files.pythonhosted.org/packages/67/ad/550ddcb8b5a5d9949fe6606595cce36984c1d42309f1e04af98f5933a7ea/Cython-3.0.12-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:4ee6f1ea1bead8e6cbc4e64571505b5d8dbdb3b58e679d31f3a84160cebf1a1a", size = 3393574 },
|
||||
{ url = "https://files.pythonhosted.org/packages/34/de/ade0a80bea17197662e23d39d3d3fbf89e9e99e6ad91fd95ab87120edb3a/Cython-3.0.12-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:57aefa6d3341109e46ec1a13e3a763aaa2cbeb14e82af2485b318194be1d9170", size = 3367198 },
|
||||
{ url = "https://files.pythonhosted.org/packages/a8/30/7f48207ea13dab46604db0dd388e807d53513ba6ad1c34462892072f8f8c/Cython-3.0.12-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:879ae9023958d63c0675015369384642d0afb9c9d1f3473df9186c42f7a9d265", size = 3535849 },
|
||||
{ url = "https://files.pythonhosted.org/packages/81/ab/f61c79fa14bd433a7dfd1548c5e00d9bd18b557c2f836aaece4fb1b22f34/Cython-3.0.12-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:36fcd584dae547de6f095500a380f4a0cce72b7a7e409e9ff03cb9beed6ac7a1", size = 3559079 },
|
||||
{ url = "https://files.pythonhosted.org/packages/d0/d1/1dbf17061229ccd35d5c0eed659fab60c2e50d2eadfa2a5729e753b6f4d0/Cython-3.0.12-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:62b79dcc0de49efe9e84b9d0e2ae0a6fc9b14691a65565da727aa2e2e63c6a28", size = 3436649 },
|
||||
{ url = "https://files.pythonhosted.org/packages/2d/d4/9ce42fff6de5550f870cdde9a1482d69ea66a1249a88fa0d0df9adebfb1a/Cython-3.0.12-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:4aa255781b093a8401109d8f2104bbb2e52de7639d5896aefafddc85c30e0894", size = 3644025 },
|
||||
{ url = "https://files.pythonhosted.org/packages/e3/89/b0c847f9df92af3ef11281b6811c000bd6f8ce0db02e4374397f8d67f829/Cython-3.0.12-cp313-cp313-win32.whl", hash = "sha256:77d48f2d4bab9fe1236eb753d18f03e8b2619af5b6f05d51df0532a92dfb38ab", size = 2604911 },
|
||||
{ url = "https://files.pythonhosted.org/packages/a6/5f/bbfaf2b5f7bf78854ecbc82f8473a3892ae5580e0c5bd0d4a82580b39ed3/Cython-3.0.12-cp313-cp313-win_amd64.whl", hash = "sha256:86c304b20bd57c727c7357e90d5ba1a2b6f1c45492de2373814d7745ef2e63b4", size = 2786786 },
|
||||
{ url = "https://files.pythonhosted.org/packages/27/6b/7c87867d255cbce8167ed99fc65635e9395d2af0f0c915428f5b17ec412d/Cython-3.0.12-py2.py3-none-any.whl", hash = "sha256:0038c9bae46c459669390e53a1ec115f8096b2e4647ae007ff1bf4e6dee92806", size = 1171640 },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "decli"
|
||||
version = "0.6.2"
|
||||
|
@ -151,6 +257,15 @@ wheels = [
|
|||
{ url = "https://files.pythonhosted.org/packages/1f/88/52c9422bc853cd7c2b6122090e887d17b5fad29b67f930e4277c9c557357/griffe-1.5.5-py3-none-any.whl", hash = "sha256:2761b1e8876c6f1f9ab1af274df93ea6bbadd65090de5f38f4cb5cc84897c7dd", size = 128221 },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "idna"
|
||||
version = "3.10"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/f1/70/7703c29685631f5a7590aa73f1f1d3fa9a380e654b86af429e0934a32f7d/idna-3.10.tar.gz", hash = "sha256:12f65c9b470abda6dc35cf8e63cc574b1c52b11df2c86030af0ac09b01b13ea9", size = 190490 }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/76/c6/c88e154df9c4e1a2a66ccf0005a88dfb2650c1dffb6f5ce603dfbd452ce3/idna-3.10-py3-none-any.whl", hash = "sha256:946d195a0d259cbba61165e88e65941f16e9b36ea6ddb97f00452bae8b1287d3", size = 70442 },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "iniconfig"
|
||||
version = "2.0.0"
|
||||
|
@ -281,6 +396,54 @@ wheels = [
|
|||
{ url = "https://files.pythonhosted.org/packages/2a/e2/5d3f6ada4297caebe1a2add3b126fe800c96f56dbe5d1988a2cbe0b267aa/mypy_extensions-1.0.0-py3-none-any.whl", hash = "sha256:4392f6c0eb8a5668a69e23d168ffa70f0be9ccfd32b5cc2d26a34ae5b844552d", size = 4695 },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "numpy"
|
||||
version = "2.2.3"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/fb/90/8956572f5c4ae52201fdec7ba2044b2c882832dcec7d5d0922c9e9acf2de/numpy-2.2.3.tar.gz", hash = "sha256:dbdc15f0c81611925f382dfa97b3bd0bc2c1ce19d4fe50482cb0ddc12ba30020", size = 20262700 }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/96/86/453aa3949eab6ff54e2405f9cb0c01f756f031c3dc2a6d60a1d40cba5488/numpy-2.2.3-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:16372619ee728ed67a2a606a614f56d3eabc5b86f8b615c79d01957062826ca8", size = 21237256 },
|
||||
{ url = "https://files.pythonhosted.org/packages/20/c3/93ecceadf3e155d6a9e4464dd2392d8d80cf436084c714dc8535121c83e8/numpy-2.2.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:5521a06a3148686d9269c53b09f7d399a5725c47bbb5b35747e1cb76326b714b", size = 14408049 },
|
||||
{ url = "https://files.pythonhosted.org/packages/8d/29/076999b69bd9264b8df5e56f2be18da2de6b2a2d0e10737e5307592e01de/numpy-2.2.3-cp311-cp311-macosx_14_0_arm64.whl", hash = "sha256:7c8dde0ca2f77828815fd1aedfdf52e59071a5bae30dac3b4da2a335c672149a", size = 5408655 },
|
||||
{ url = "https://files.pythonhosted.org/packages/e2/a7/b14f0a73eb0fe77cb9bd5b44534c183b23d4229c099e339c522724b02678/numpy-2.2.3-cp311-cp311-macosx_14_0_x86_64.whl", hash = "sha256:77974aba6c1bc26e3c205c2214f0d5b4305bdc719268b93e768ddb17e3fdd636", size = 6949996 },
|
||||
{ url = "https://files.pythonhosted.org/packages/72/2f/8063da0616bb0f414b66dccead503bd96e33e43685c820e78a61a214c098/numpy-2.2.3-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d42f9c36d06440e34226e8bd65ff065ca0963aeecada587b937011efa02cdc9d", size = 14355789 },
|
||||
{ url = "https://files.pythonhosted.org/packages/e6/d7/3cd47b00b8ea95ab358c376cf5602ad21871410950bc754cf3284771f8b6/numpy-2.2.3-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f2712c5179f40af9ddc8f6727f2bd910ea0eb50206daea75f58ddd9fa3f715bb", size = 16411356 },
|
||||
{ url = "https://files.pythonhosted.org/packages/27/c0/a2379e202acbb70b85b41483a422c1e697ff7eee74db642ca478de4ba89f/numpy-2.2.3-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:c8b0451d2ec95010d1db8ca733afc41f659f425b7f608af569711097fd6014e2", size = 15576770 },
|
||||
{ url = "https://files.pythonhosted.org/packages/bc/63/a13ee650f27b7999e5b9e1964ae942af50bb25606d088df4229283eda779/numpy-2.2.3-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:d9b4a8148c57ecac25a16b0e11798cbe88edf5237b0df99973687dd866f05e1b", size = 18200483 },
|
||||
{ url = "https://files.pythonhosted.org/packages/4c/87/e71f89935e09e8161ac9c590c82f66d2321eb163893a94af749dfa8a3cf8/numpy-2.2.3-cp311-cp311-win32.whl", hash = "sha256:1f45315b2dc58d8a3e7754fe4e38b6fce132dab284a92851e41b2b344f6441c5", size = 6588415 },
|
||||
{ url = "https://files.pythonhosted.org/packages/b9/c6/cd4298729826af9979c5f9ab02fcaa344b82621e7c49322cd2d210483d3f/numpy-2.2.3-cp311-cp311-win_amd64.whl", hash = "sha256:9f48ba6f6c13e5e49f3d3efb1b51c8193215c42ac82610a04624906a9270be6f", size = 12929604 },
|
||||
{ url = "https://files.pythonhosted.org/packages/43/ec/43628dcf98466e087812142eec6d1c1a6c6bdfdad30a0aa07b872dc01f6f/numpy-2.2.3-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:12c045f43b1d2915eca6b880a7f4a256f59d62df4f044788c8ba67709412128d", size = 20929458 },
|
||||
{ url = "https://files.pythonhosted.org/packages/9b/c0/2f4225073e99a5c12350954949ed19b5d4a738f541d33e6f7439e33e98e4/numpy-2.2.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:87eed225fd415bbae787f93a457af7f5990b92a334e346f72070bf569b9c9c95", size = 14115299 },
|
||||
{ url = "https://files.pythonhosted.org/packages/ca/fa/d2c5575d9c734a7376cc1592fae50257ec95d061b27ee3dbdb0b3b551eb2/numpy-2.2.3-cp312-cp312-macosx_14_0_arm64.whl", hash = "sha256:712a64103d97c404e87d4d7c47fb0c7ff9acccc625ca2002848e0d53288b90ea", size = 5145723 },
|
||||
{ url = "https://files.pythonhosted.org/packages/eb/dc/023dad5b268a7895e58e791f28dc1c60eb7b6c06fcbc2af8538ad069d5f3/numpy-2.2.3-cp312-cp312-macosx_14_0_x86_64.whl", hash = "sha256:a5ae282abe60a2db0fd407072aff4599c279bcd6e9a2475500fc35b00a57c532", size = 6678797 },
|
||||
{ url = "https://files.pythonhosted.org/packages/3f/19/bcd641ccf19ac25abb6fb1dcd7744840c11f9d62519d7057b6ab2096eb60/numpy-2.2.3-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5266de33d4c3420973cf9ae3b98b54a2a6d53a559310e3236c4b2b06b9c07d4e", size = 14067362 },
|
||||
{ url = "https://files.pythonhosted.org/packages/39/04/78d2e7402fb479d893953fb78fa7045f7deb635ec095b6b4f0260223091a/numpy-2.2.3-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3b787adbf04b0db1967798dba8da1af07e387908ed1553a0d6e74c084d1ceafe", size = 16116679 },
|
||||
{ url = "https://files.pythonhosted.org/packages/d0/a1/e90f7aa66512be3150cb9d27f3d9995db330ad1b2046474a13b7040dfd92/numpy-2.2.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:34c1b7e83f94f3b564b35f480f5652a47007dd91f7c839f404d03279cc8dd021", size = 15264272 },
|
||||
{ url = "https://files.pythonhosted.org/packages/dc/b6/50bd027cca494de4fa1fc7bf1662983d0ba5f256fa0ece2c376b5eb9b3f0/numpy-2.2.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:4d8335b5f1b6e2bce120d55fb17064b0262ff29b459e8493d1785c18ae2553b8", size = 17880549 },
|
||||
{ url = "https://files.pythonhosted.org/packages/96/30/f7bf4acb5f8db10a96f73896bdeed7a63373137b131ca18bd3dab889db3b/numpy-2.2.3-cp312-cp312-win32.whl", hash = "sha256:4d9828d25fb246bedd31e04c9e75714a4087211ac348cb39c8c5f99dbb6683fe", size = 6293394 },
|
||||
{ url = "https://files.pythonhosted.org/packages/42/6e/55580a538116d16ae7c9aa17d4edd56e83f42126cb1dfe7a684da7925d2c/numpy-2.2.3-cp312-cp312-win_amd64.whl", hash = "sha256:83807d445817326b4bcdaaaf8e8e9f1753da04341eceec705c001ff342002e5d", size = 12626357 },
|
||||
{ url = "https://files.pythonhosted.org/packages/0e/8b/88b98ed534d6a03ba8cddb316950fe80842885709b58501233c29dfa24a9/numpy-2.2.3-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:7bfdb06b395385ea9b91bf55c1adf1b297c9fdb531552845ff1d3ea6e40d5aba", size = 20916001 },
|
||||
{ url = "https://files.pythonhosted.org/packages/d9/b4/def6ec32c725cc5fbd8bdf8af80f616acf075fe752d8a23e895da8c67b70/numpy-2.2.3-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:23c9f4edbf4c065fddb10a4f6e8b6a244342d95966a48820c614891e5059bb50", size = 14130721 },
|
||||
{ url = "https://files.pythonhosted.org/packages/20/60/70af0acc86495b25b672d403e12cb25448d79a2b9658f4fc45e845c397a8/numpy-2.2.3-cp313-cp313-macosx_14_0_arm64.whl", hash = "sha256:a0c03b6be48aaf92525cccf393265e02773be8fd9551a2f9adbe7db1fa2b60f1", size = 5130999 },
|
||||
{ url = "https://files.pythonhosted.org/packages/2e/69/d96c006fb73c9a47bcb3611417cf178049aae159afae47c48bd66df9c536/numpy-2.2.3-cp313-cp313-macosx_14_0_x86_64.whl", hash = "sha256:2376e317111daa0a6739e50f7ee2a6353f768489102308b0d98fcf4a04f7f3b5", size = 6665299 },
|
||||
{ url = "https://files.pythonhosted.org/packages/5a/3f/d8a877b6e48103733ac224ffa26b30887dc9944ff95dffdfa6c4ce3d7df3/numpy-2.2.3-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8fb62fe3d206d72fe1cfe31c4a1106ad2b136fcc1606093aeab314f02930fdf2", size = 14064096 },
|
||||
{ url = "https://files.pythonhosted.org/packages/e4/43/619c2c7a0665aafc80efca465ddb1f260287266bdbdce517396f2f145d49/numpy-2.2.3-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:52659ad2534427dffcc36aac76bebdd02b67e3b7a619ac67543bc9bfe6b7cdb1", size = 16114758 },
|
||||
{ url = "https://files.pythonhosted.org/packages/d9/79/ee4fe4f60967ccd3897aa71ae14cdee9e3c097e3256975cc9575d393cb42/numpy-2.2.3-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:1b416af7d0ed3271cad0f0a0d0bee0911ed7eba23e66f8424d9f3dfcdcae1304", size = 15259880 },
|
||||
{ url = "https://files.pythonhosted.org/packages/fb/c8/8b55cf05db6d85b7a7d414b3d1bd5a740706df00bfa0824a08bf041e52ee/numpy-2.2.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:1402da8e0f435991983d0a9708b779f95a8c98c6b18a171b9f1be09005e64d9d", size = 17876721 },
|
||||
{ url = "https://files.pythonhosted.org/packages/21/d6/b4c2f0564b7dcc413117b0ffbb818d837e4b29996b9234e38b2025ed24e7/numpy-2.2.3-cp313-cp313-win32.whl", hash = "sha256:136553f123ee2951bfcfbc264acd34a2fc2f29d7cdf610ce7daf672b6fbaa693", size = 6290195 },
|
||||
{ url = "https://files.pythonhosted.org/packages/97/e7/7d55a86719d0de7a6a597949f3febefb1009435b79ba510ff32f05a8c1d7/numpy-2.2.3-cp313-cp313-win_amd64.whl", hash = "sha256:5b732c8beef1d7bc2d9e476dbba20aaff6167bf205ad9aa8d30913859e82884b", size = 12619013 },
|
||||
{ url = "https://files.pythonhosted.org/packages/a6/1f/0b863d5528b9048fd486a56e0b97c18bf705e88736c8cea7239012119a54/numpy-2.2.3-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:435e7a933b9fda8126130b046975a968cc2d833b505475e588339e09f7672890", size = 20944621 },
|
||||
{ url = "https://files.pythonhosted.org/packages/aa/99/b478c384f7a0a2e0736177aafc97dc9152fc036a3fdb13f5a3ab225f1494/numpy-2.2.3-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:7678556eeb0152cbd1522b684dcd215250885993dd00adb93679ec3c0e6e091c", size = 14142502 },
|
||||
{ url = "https://files.pythonhosted.org/packages/fb/61/2d9a694a0f9cd0a839501d362de2a18de75e3004576a3008e56bdd60fcdb/numpy-2.2.3-cp313-cp313t-macosx_14_0_arm64.whl", hash = "sha256:2e8da03bd561504d9b20e7a12340870dfc206c64ea59b4cfee9fceb95070ee94", size = 5176293 },
|
||||
{ url = "https://files.pythonhosted.org/packages/33/35/51e94011b23e753fa33f891f601e5c1c9a3d515448659b06df9d40c0aa6e/numpy-2.2.3-cp313-cp313t-macosx_14_0_x86_64.whl", hash = "sha256:c9aa4496fd0e17e3843399f533d62857cef5900facf93e735ef65aa4bbc90ef0", size = 6691874 },
|
||||
{ url = "https://files.pythonhosted.org/packages/ff/cf/06e37619aad98a9d03bd8d65b8e3041c3a639be0f5f6b0a0e2da544538d4/numpy-2.2.3-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f4ca91d61a4bf61b0f2228f24bbfa6a9facd5f8af03759fe2a655c50ae2c6610", size = 14036826 },
|
||||
{ url = "https://files.pythonhosted.org/packages/0c/93/5d7d19955abd4d6099ef4a8ee006f9ce258166c38af259f9e5558a172e3e/numpy-2.2.3-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:deaa09cd492e24fd9b15296844c0ad1b3c976da7907e1c1ed3a0ad21dded6f76", size = 16096567 },
|
||||
{ url = "https://files.pythonhosted.org/packages/af/53/d1c599acf7732d81f46a93621dab6aa8daad914b502a7a115b3f17288ab2/numpy-2.2.3-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:246535e2f7496b7ac85deffe932896a3577be7af8fb7eebe7146444680297e9a", size = 15242514 },
|
||||
{ url = "https://files.pythonhosted.org/packages/53/43/c0f5411c7b3ea90adf341d05ace762dad8cb9819ef26093e27b15dd121ac/numpy-2.2.3-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:daf43a3d1ea699402c5a850e5313680ac355b4adc9770cd5cfc2940e7861f1bf", size = 17872920 },
|
||||
{ url = "https://files.pythonhosted.org/packages/5b/57/6dbdd45ab277aff62021cafa1e15f9644a52f5b5fc840bc7591b4079fb58/numpy-2.2.3-cp313-cp313t-win32.whl", hash = "sha256:cf802eef1f0134afb81fef94020351be4fe1d6681aadf9c5e862af6602af64ef", size = 6346584 },
|
||||
{ url = "https://files.pythonhosted.org/packages/97/9b/484f7d04b537d0a1202a5ba81c6f53f1846ae6c63c2127f8df869ed31342/numpy-2.2.3-cp313-cp313t-win_amd64.whl", hash = "sha256:aee2512827ceb6d7f517c8b85aa5d3923afe8fc7a57d028cffcd522f1c6fd082", size = 12706784 },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "packaging"
|
||||
version = "24.2"
|
||||
|
@ -311,6 +474,15 @@ wheels = [
|
|||
{ url = "https://files.pythonhosted.org/packages/a9/6a/fd08d94654f7e67c52ca30523a178b3f8ccc4237fce4be90d39c938a831a/prompt_toolkit-3.0.48-py3-none-any.whl", hash = "sha256:f49a827f90062e411f1ce1f854f2aedb3c23353244f8108b89283587397ac10e", size = 386595 },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pycparser"
|
||||
version = "2.22"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/1d/b2/31537cf4b1ca988837256c910a668b553fceb8f069bedc4b1c826024b52c/pycparser-2.22.tar.gz", hash = "sha256:491c8be9c040f5390f5bf44a5b07752bd07f56edf992381b05c701439eec10f6", size = 172736 }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/13/a3/a812df4e2dd5696d1f351d58b8fe16a405b234ad2886a0dab9183fb78109/pycparser-2.22-py3-none-any.whl", hash = "sha256:c3702b6d3dd8c7abc1afa565d7e63d53a1d0bd86cdc24edd75470f4de499cfcc", size = 117552 },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pydantic"
|
||||
version = "2.10.5"
|
||||
|
@ -449,6 +621,21 @@ wheels = [
|
|||
{ url = "https://files.pythonhosted.org/packages/ad/3f/11dd4cd4f39e05128bfd20138faea57bec56f9ffba6185d276e3107ba5b2/questionary-2.1.0-py3-none-any.whl", hash = "sha256:44174d237b68bc828e4878c763a9ad6790ee61990e0ae72927694ead57bab8ec", size = 36747 },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "requests"
|
||||
version = "2.32.3"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
dependencies = [
|
||||
{ name = "certifi" },
|
||||
{ name = "charset-normalizer" },
|
||||
{ name = "idna" },
|
||||
{ name = "urllib3" },
|
||||
]
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/63/70/2bf7780ad2d390a8d301ad0b550f1581eadbd9a20f896afe06353c2a2913/requests-2.32.3.tar.gz", hash = "sha256:55365417734eb18255590a9ff9eb97e9e1da868d4ccd6402399eaf68af20a760", size = 131218 }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/f9/9b/335f9764261e915ed497fcdeb11df5dfd6f7bf257d4a6a2a686d80da4d54/requests-2.32.3-py3-none-any.whl", hash = "sha256:70761cfe03c773ceb22aa2f671b4757976145175cdfca038c02654d061d6dcc6", size = 64928 },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rich"
|
||||
version = "13.9.4"
|
||||
|
@ -514,6 +701,15 @@ wheels = [
|
|||
{ url = "https://files.pythonhosted.org/packages/26/9f/ad63fc0248c5379346306f8668cda6e2e2e9c95e01216d2b8ffd9ff037d0/typing_extensions-4.12.2-py3-none-any.whl", hash = "sha256:04e5ca0351e0f3f85c6853954072df659d0d13fac324d0072316b67d7794700d", size = 37438 },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "urllib3"
|
||||
version = "2.3.0"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/aa/63/e53da845320b757bf29ef6a9062f5c669fe997973f966045cb019c3f4b66/urllib3-2.3.0.tar.gz", hash = "sha256:f8c5449b3cf0861679ce7e0503c7b44b5ec981bec0d1d3795a07f1ba96f0204d", size = 307268 }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/c8/19/4ec628951a74043532ca2cf5d97b7b14863931476d117c471e8e2b1eb39f/urllib3-2.3.0-py3-none-any.whl", hash = "sha256:1cee9ad369867bfdbbb48b7dd50374c0967a0bb7710050facf0dd6911440e3df", size = 128369 },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wcwidth"
|
||||
version = "0.2.13"
|
||||
|
@ -522,3 +718,62 @@ sdist = { url = "https://files.pythonhosted.org/packages/6c/63/53559446a878410fc
|
|||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/fd/84/fd2ba7aafacbad3c4201d395674fc6348826569da3c0937e75505ead3528/wcwidth-0.2.13-py2.py3-none-any.whl", hash = "sha256:3da69048e4540d84af32131829ff948f1e022c1c6bdb8d6102117aac784f6859", size = 34166 },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "zstandard"
|
||||
version = "0.23.0"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
dependencies = [
|
||||
{ name = "cffi", marker = "platform_python_implementation == 'PyPy'" },
|
||||
]
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/ed/f6/2ac0287b442160a89d726b17a9184a4c615bb5237db763791a7fd16d9df1/zstandard-0.23.0.tar.gz", hash = "sha256:b2d8c62d08e7255f68f7a740bae85b3c9b8e5466baa9cbf7f57f1cde0ac6bc09", size = 681701 }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/9e/40/f67e7d2c25a0e2dc1744dd781110b0b60306657f8696cafb7ad7579469bd/zstandard-0.23.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:34895a41273ad33347b2fc70e1bff4240556de3c46c6ea430a7ed91f9042aa4e", size = 788699 },
|
||||
{ url = "https://files.pythonhosted.org/packages/e8/46/66d5b55f4d737dd6ab75851b224abf0afe5774976fe511a54d2eb9063a41/zstandard-0.23.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:77ea385f7dd5b5676d7fd943292ffa18fbf5c72ba98f7d09fc1fb9e819b34c23", size = 633681 },
|
||||
{ url = "https://files.pythonhosted.org/packages/63/b6/677e65c095d8e12b66b8f862b069bcf1f1d781b9c9c6f12eb55000d57583/zstandard-0.23.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:983b6efd649723474f29ed42e1467f90a35a74793437d0bc64a5bf482bedfa0a", size = 4944328 },
|
||||
{ url = "https://files.pythonhosted.org/packages/59/cc/e76acb4c42afa05a9d20827116d1f9287e9c32b7ad58cc3af0721ce2b481/zstandard-0.23.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:80a539906390591dd39ebb8d773771dc4db82ace6372c4d41e2d293f8e32b8db", size = 5311955 },
|
||||
{ url = "https://files.pythonhosted.org/packages/78/e4/644b8075f18fc7f632130c32e8f36f6dc1b93065bf2dd87f03223b187f26/zstandard-0.23.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:445e4cb5048b04e90ce96a79b4b63140e3f4ab5f662321975679b5f6360b90e2", size = 5344944 },
|
||||
{ url = "https://files.pythonhosted.org/packages/76/3f/dbafccf19cfeca25bbabf6f2dd81796b7218f768ec400f043edc767015a6/zstandard-0.23.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fd30d9c67d13d891f2360b2a120186729c111238ac63b43dbd37a5a40670b8ca", size = 5442927 },
|
||||
{ url = "https://files.pythonhosted.org/packages/0c/c3/d24a01a19b6733b9f218e94d1a87c477d523237e07f94899e1c10f6fd06c/zstandard-0.23.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d20fd853fbb5807c8e84c136c278827b6167ded66c72ec6f9a14b863d809211c", size = 4864910 },
|
||||
{ url = "https://files.pythonhosted.org/packages/1c/a9/cf8f78ead4597264f7618d0875be01f9bc23c9d1d11afb6d225b867cb423/zstandard-0.23.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:ed1708dbf4d2e3a1c5c69110ba2b4eb6678262028afd6c6fbcc5a8dac9cda68e", size = 4935544 },
|
||||
{ url = "https://files.pythonhosted.org/packages/2c/96/8af1e3731b67965fb995a940c04a2c20997a7b3b14826b9d1301cf160879/zstandard-0.23.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:be9b5b8659dff1f913039c2feee1aca499cfbc19e98fa12bc85e037c17ec6ca5", size = 5467094 },
|
||||
{ url = "https://files.pythonhosted.org/packages/ff/57/43ea9df642c636cb79f88a13ab07d92d88d3bfe3e550b55a25a07a26d878/zstandard-0.23.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:65308f4b4890aa12d9b6ad9f2844b7ee42c7f7a4fd3390425b242ffc57498f48", size = 4860440 },
|
||||
{ url = "https://files.pythonhosted.org/packages/46/37/edb78f33c7f44f806525f27baa300341918fd4c4af9472fbc2c3094be2e8/zstandard-0.23.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:98da17ce9cbf3bfe4617e836d561e433f871129e3a7ac16d6ef4c680f13a839c", size = 4700091 },
|
||||
{ url = "https://files.pythonhosted.org/packages/c1/f1/454ac3962671a754f3cb49242472df5c2cced4eb959ae203a377b45b1a3c/zstandard-0.23.0-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:8ed7d27cb56b3e058d3cf684d7200703bcae623e1dcc06ed1e18ecda39fee003", size = 5208682 },
|
||||
{ url = "https://files.pythonhosted.org/packages/85/b2/1734b0fff1634390b1b887202d557d2dd542de84a4c155c258cf75da4773/zstandard-0.23.0-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:b69bb4f51daf461b15e7b3db033160937d3ff88303a7bc808c67bbc1eaf98c78", size = 5669707 },
|
||||
{ url = "https://files.pythonhosted.org/packages/52/5a/87d6971f0997c4b9b09c495bf92189fb63de86a83cadc4977dc19735f652/zstandard-0.23.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:034b88913ecc1b097f528e42b539453fa82c3557e414b3de9d5632c80439a473", size = 5201792 },
|
||||
{ url = "https://files.pythonhosted.org/packages/79/02/6f6a42cc84459d399bd1a4e1adfc78d4dfe45e56d05b072008d10040e13b/zstandard-0.23.0-cp311-cp311-win32.whl", hash = "sha256:f2d4380bf5f62daabd7b751ea2339c1a21d1c9463f1feb7fc2bdcea2c29c3160", size = 430586 },
|
||||
{ url = "https://files.pythonhosted.org/packages/be/a2/4272175d47c623ff78196f3c10e9dc7045c1b9caf3735bf041e65271eca4/zstandard-0.23.0-cp311-cp311-win_amd64.whl", hash = "sha256:62136da96a973bd2557f06ddd4e8e807f9e13cbb0bfb9cc06cfe6d98ea90dfe0", size = 495420 },
|
||||
{ url = "https://files.pythonhosted.org/packages/7b/83/f23338c963bd9de687d47bf32efe9fd30164e722ba27fb59df33e6b1719b/zstandard-0.23.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:b4567955a6bc1b20e9c31612e615af6b53733491aeaa19a6b3b37f3b65477094", size = 788713 },
|
||||
{ url = "https://files.pythonhosted.org/packages/5b/b3/1a028f6750fd9227ee0b937a278a434ab7f7fdc3066c3173f64366fe2466/zstandard-0.23.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:1e172f57cd78c20f13a3415cc8dfe24bf388614324d25539146594c16d78fcc8", size = 633459 },
|
||||
{ url = "https://files.pythonhosted.org/packages/26/af/36d89aae0c1f95a0a98e50711bc5d92c144939efc1f81a2fcd3e78d7f4c1/zstandard-0.23.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b0e166f698c5a3e914947388c162be2583e0c638a4703fc6a543e23a88dea3c1", size = 4945707 },
|
||||
{ url = "https://files.pythonhosted.org/packages/cd/2e/2051f5c772f4dfc0aae3741d5fc72c3dcfe3aaeb461cc231668a4db1ce14/zstandard-0.23.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:12a289832e520c6bd4dcaad68e944b86da3bad0d339ef7989fb7e88f92e96072", size = 5306545 },
|
||||
{ url = "https://files.pythonhosted.org/packages/0a/9e/a11c97b087f89cab030fa71206963090d2fecd8eb83e67bb8f3ffb84c024/zstandard-0.23.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d50d31bfedd53a928fed6707b15a8dbeef011bb6366297cc435accc888b27c20", size = 5337533 },
|
||||
{ url = "https://files.pythonhosted.org/packages/fc/79/edeb217c57fe1bf16d890aa91a1c2c96b28c07b46afed54a5dcf310c3f6f/zstandard-0.23.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:72c68dda124a1a138340fb62fa21b9bf4848437d9ca60bd35db36f2d3345f373", size = 5436510 },
|
||||
{ url = "https://files.pythonhosted.org/packages/81/4f/c21383d97cb7a422ddf1ae824b53ce4b51063d0eeb2afa757eb40804a8ef/zstandard-0.23.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:53dd9d5e3d29f95acd5de6802e909ada8d8d8cfa37a3ac64836f3bc4bc5512db", size = 4859973 },
|
||||
{ url = "https://files.pythonhosted.org/packages/ab/15/08d22e87753304405ccac8be2493a495f529edd81d39a0870621462276ef/zstandard-0.23.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:6a41c120c3dbc0d81a8e8adc73312d668cd34acd7725f036992b1b72d22c1772", size = 4936968 },
|
||||
{ url = "https://files.pythonhosted.org/packages/eb/fa/f3670a597949fe7dcf38119a39f7da49a8a84a6f0b1a2e46b2f71a0ab83f/zstandard-0.23.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:40b33d93c6eddf02d2c19f5773196068d875c41ca25730e8288e9b672897c105", size = 5467179 },
|
||||
{ url = "https://files.pythonhosted.org/packages/4e/a9/dad2ab22020211e380adc477a1dbf9f109b1f8d94c614944843e20dc2a99/zstandard-0.23.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:9206649ec587e6b02bd124fb7799b86cddec350f6f6c14bc82a2b70183e708ba", size = 4848577 },
|
||||
{ url = "https://files.pythonhosted.org/packages/08/03/dd28b4484b0770f1e23478413e01bee476ae8227bbc81561f9c329e12564/zstandard-0.23.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:76e79bc28a65f467e0409098fa2c4376931fd3207fbeb6b956c7c476d53746dd", size = 4693899 },
|
||||
{ url = "https://files.pythonhosted.org/packages/2b/64/3da7497eb635d025841e958bcd66a86117ae320c3b14b0ae86e9e8627518/zstandard-0.23.0-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:66b689c107857eceabf2cf3d3fc699c3c0fe8ccd18df2219d978c0283e4c508a", size = 5199964 },
|
||||
{ url = "https://files.pythonhosted.org/packages/43/a4/d82decbab158a0e8a6ebb7fc98bc4d903266bce85b6e9aaedea1d288338c/zstandard-0.23.0-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:9c236e635582742fee16603042553d276cca506e824fa2e6489db04039521e90", size = 5655398 },
|
||||
{ url = "https://files.pythonhosted.org/packages/f2/61/ac78a1263bc83a5cf29e7458b77a568eda5a8f81980691bbc6eb6a0d45cc/zstandard-0.23.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:a8fffdbd9d1408006baaf02f1068d7dd1f016c6bcb7538682622c556e7b68e35", size = 5191313 },
|
||||
{ url = "https://files.pythonhosted.org/packages/e7/54/967c478314e16af5baf849b6ee9d6ea724ae5b100eb506011f045d3d4e16/zstandard-0.23.0-cp312-cp312-win32.whl", hash = "sha256:dc1d33abb8a0d754ea4763bad944fd965d3d95b5baef6b121c0c9013eaf1907d", size = 430877 },
|
||||
{ url = "https://files.pythonhosted.org/packages/75/37/872d74bd7739639c4553bf94c84af7d54d8211b626b352bc57f0fd8d1e3f/zstandard-0.23.0-cp312-cp312-win_amd64.whl", hash = "sha256:64585e1dba664dc67c7cdabd56c1e5685233fbb1fc1966cfba2a340ec0dfff7b", size = 495595 },
|
||||
{ url = "https://files.pythonhosted.org/packages/80/f1/8386f3f7c10261fe85fbc2c012fdb3d4db793b921c9abcc995d8da1b7a80/zstandard-0.23.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:576856e8594e6649aee06ddbfc738fec6a834f7c85bf7cadd1c53d4a58186ef9", size = 788975 },
|
||||
{ url = "https://files.pythonhosted.org/packages/16/e8/cbf01077550b3e5dc86089035ff8f6fbbb312bc0983757c2d1117ebba242/zstandard-0.23.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:38302b78a850ff82656beaddeb0bb989a0322a8bbb1bf1ab10c17506681d772a", size = 633448 },
|
||||
{ url = "https://files.pythonhosted.org/packages/06/27/4a1b4c267c29a464a161aeb2589aff212b4db653a1d96bffe3598f3f0d22/zstandard-0.23.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d2240ddc86b74966c34554c49d00eaafa8200a18d3a5b6ffbf7da63b11d74ee2", size = 4945269 },
|
||||
{ url = "https://files.pythonhosted.org/packages/7c/64/d99261cc57afd9ae65b707e38045ed8269fbdae73544fd2e4a4d50d0ed83/zstandard-0.23.0-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:2ef230a8fd217a2015bc91b74f6b3b7d6522ba48be29ad4ea0ca3a3775bf7dd5", size = 5306228 },
|
||||
{ url = "https://files.pythonhosted.org/packages/7a/cf/27b74c6f22541f0263016a0fd6369b1b7818941de639215c84e4e94b2a1c/zstandard-0.23.0-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:774d45b1fac1461f48698a9d4b5fa19a69d47ece02fa469825b442263f04021f", size = 5336891 },
|
||||
{ url = "https://files.pythonhosted.org/packages/fa/18/89ac62eac46b69948bf35fcd90d37103f38722968e2981f752d69081ec4d/zstandard-0.23.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6f77fa49079891a4aab203d0b1744acc85577ed16d767b52fc089d83faf8d8ed", size = 5436310 },
|
||||
{ url = "https://files.pythonhosted.org/packages/a8/a8/5ca5328ee568a873f5118d5b5f70d1f36c6387716efe2e369010289a5738/zstandard-0.23.0-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ac184f87ff521f4840e6ea0b10c0ec90c6b1dcd0bad2f1e4a9a1b4fa177982ea", size = 4859912 },
|
||||
{ url = "https://files.pythonhosted.org/packages/ea/ca/3781059c95fd0868658b1cf0440edd832b942f84ae60685d0cfdb808bca1/zstandard-0.23.0-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:c363b53e257246a954ebc7c488304b5592b9c53fbe74d03bc1c64dda153fb847", size = 4936946 },
|
||||
{ url = "https://files.pythonhosted.org/packages/ce/11/41a58986f809532742c2b832c53b74ba0e0a5dae7e8ab4642bf5876f35de/zstandard-0.23.0-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:e7792606d606c8df5277c32ccb58f29b9b8603bf83b48639b7aedf6df4fe8171", size = 5466994 },
|
||||
{ url = "https://files.pythonhosted.org/packages/83/e3/97d84fe95edd38d7053af05159465d298c8b20cebe9ccb3d26783faa9094/zstandard-0.23.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:a0817825b900fcd43ac5d05b8b3079937073d2b1ff9cf89427590718b70dd840", size = 4848681 },
|
||||
{ url = "https://files.pythonhosted.org/packages/6e/99/cb1e63e931de15c88af26085e3f2d9af9ce53ccafac73b6e48418fd5a6e6/zstandard-0.23.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:9da6bc32faac9a293ddfdcb9108d4b20416219461e4ec64dfea8383cac186690", size = 4694239 },
|
||||
{ url = "https://files.pythonhosted.org/packages/ab/50/b1e703016eebbc6501fc92f34db7b1c68e54e567ef39e6e59cf5fb6f2ec0/zstandard-0.23.0-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:fd7699e8fd9969f455ef2926221e0233f81a2542921471382e77a9e2f2b57f4b", size = 5200149 },
|
||||
{ url = "https://files.pythonhosted.org/packages/aa/e0/932388630aaba70197c78bdb10cce2c91fae01a7e553b76ce85471aec690/zstandard-0.23.0-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:d477ed829077cd945b01fc3115edd132c47e6540ddcd96ca169facff28173057", size = 5655392 },
|
||||
{ url = "https://files.pythonhosted.org/packages/02/90/2633473864f67a15526324b007a9f96c96f56d5f32ef2a56cc12f9548723/zstandard-0.23.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:fa6ce8b52c5987b3e34d5674b0ab529a4602b632ebab0a93b07bfb4dfc8f8a33", size = 5191299 },
|
||||
{ url = "https://files.pythonhosted.org/packages/b0/4c/315ca5c32da7e2dc3455f3b2caee5c8c2246074a61aac6ec3378a97b7136/zstandard-0.23.0-cp313-cp313-win32.whl", hash = "sha256:a9b07268d0c3ca5c170a385a0ab9fb7fdd9f5fd866be004c4ea39e44edce47dd", size = 430862 },
|
||||
{ url = "https://files.pythonhosted.org/packages/a2/bf/c6aaba098e2d04781e8f4f7c0ba3c7aa73d00e4c436bcc0cf059a66691d1/zstandard-0.23.0-cp313-cp313-win_amd64.whl", hash = "sha256:f3513916e8c645d0610815c257cbfd3242adfd5c4cfa78be514e5a3ebb42a41b", size = 495578 },
|
||||
]
|
||||
|
|
Loading…
Reference in New Issue