Skip to main content

A multi-purpose application framework embodying beauty in form.

Project description

Tiferet - A Python Framework for Domain-Driven Design

Introduction

Tiferet is a Python framework that elegantly distills Domain-Driven Design (DDD) into a practical, powerful tool. Drawing inspiration from the concept of beauty in balance as expressed in Kabbalah, Tiferet weaves purpose and functionality into software that not only performs but resonates deeply with its intended vision. As a cornerstone for crafting diverse applications, Tiferet empowers developers to build solutions with clarity, grace, and thoughtful design.

Tiferet embraces the complexity of real-world processes through DDD, transforming intricate business logic and evolving requirements into clear, manageable models. Far from merely navigating this labyrinth, Tiferet provides a graceful path to craft software that reflects its intended purpose with wisdom and precision, embodying beauty and balance in form and function. This tutorial guides you through building a simple calculator application, demonstrating how Tiferet harmonizes code and concept. By defining domain events and their configurations, you'll create a robust and extensible calculator that resonates with Tiferet's philosophy.

Getting Started with Tiferet

Embark on your Tiferet journey with a few simple steps to set up your Python environment. Whether you're new to Python or a seasoned developer, these instructions will prepare you to craft a calculator application with grace and precision.

Installing Python

Tiferet requires Python 3.10 or later. Follow these steps to install it:

Windows

Visit python.org, navigate to the Downloads section, and select the Python 3.10 installer for Windows. Run the installer, ensuring you check "Add Python 3.10 to PATH," then click "Install Now."

macOS

Download the Python 3.10 installer from python.org. Open the .pkg file and follow the installation prompts.

Linux (Ubuntu/Debian)

sudo apt update
sudo apt install software-properties-common
sudo add-apt-repository ppa:deadsnakes/ppa
sudo apt update
sudo apt install python3.10

Verify the installation by running:

python3.10 --version

You should see Python 3.10.x if successful.

Setting Up a Virtual Environment

To keep your project dependencies organized, create a virtual environment named tiferet_app for your calculator application:

Create the Environment

# Windows
python -m venv tiferet_app

# macOS/Linux
python3.10 -m venv tiferet_app

Activate the Environment

Activate the environment to isolate your project's dependencies:

# Windows (Command Prompt)
tiferet_app\Scripts\activate

# Windows (PowerShell)
.\tiferet_app\Scripts\Activate.ps1

# macOS/Linux
source tiferet_app/bin/activate

Your terminal should display (tiferet_app), confirming the environment is active. You can now install Tiferet and other dependencies without affecting your system’s Python setup. Deactivate the Environment When finished, deactivate the environment with: deactivate

Your First Calculator App

With your tiferet_app virtual environment activated, you're ready to install Tiferet and start building your calculator application. Follow these steps to set up your project and begin crafting with Tiferet’s elegant approach.

Installing Tiferet

Install the Tiferet package using pip in your activated virtual environment:

# Windows
pip install tiferet

# macOS/Linux
pip3 install tiferet

Project Structure

Create a project directory structure to organize your calculator application:

project_root/
├── basic_calc.py
├── calc_cli.py
└── app/
    ├── events/
    │   ├── __init__.py
    │   ├── calc.py
    │   └── settings.py
    └── configs/
        ├── __init__.py
        ├── app.yml
        ├── cli.yml
        ├── di.yml
        ├── error.yml
        ├── feature.yml
        └── logging.yml

The app/events/ directory holds domain event classes for arithmetic operations (calc.py) and input validation (settings.py). The app/configs/ directory contains configuration files for application settings (app.yml), CLI commands (cli.yml), dependency injection (di.yml), error handling (error.yml), feature workflows (feature.yml), and logging (logging.yml). The basic_calc.py script at the root initializes and runs the application. We recommend keeping the app directory name for internal projects to ensure consistency, though it can be customized for package releases. The calc_cli.py script provides a flexible command-line interface, easily integrated with shell scripts or external systems.

Crafting the Calculator Application

With Tiferet installed and your project structured, it's time to build your calculator application. This section guides you through creating a base domain event for numeric validation, defining arithmetic domain events, and setting up the application's behavior with configurations. By weaving together domain events and configurations, you'll experience Tiferet's elegant design, harmonizing functionality with clarity and precision.

Defining Base and Arithmetic Domain Event Classes

Start by creating domain event classes for numeric validation and arithmetic operations. The BasicCalcEvent in app/events/settings.py provides validation logic, while arithmetic domain events (AddNumber, SubtractNumber, MultiplyNumber, DivideNumber, ExponentiateNumber) in app/events/calc.py perform calculations. All arithmetic domain events inherit from BasicCalcEvent, which extends Tiferet's DomainEvent class, ensuring robust numeric validation and core event functionality.

Base Domain Event in events/settings.py

Numeric validation is critical to ensure the calculator handles inputs correctly, preventing errors from invalid data. The BasicCalcEvent class centralizes validation logic, making it reusable across all arithmetic domain events by converting string inputs to integers or floats.

Create app/events/settings.py with the following contents:

# *** imports

# ** infra
from tiferet.events import *

# *** events

# ** event: basic_calc_event
class BasicCalcEvent(DomainEvent):
    '''
    A domain event to validate that a value can be a Number object.
    '''
    
    # * method: verify_number
    def verify_number(self, value: str) -> int | float:
        '''
        Verify that the value can be converted to an integer or float.
        
        :param value: The value to verify.
        :type value: str
        :return: The numeric value as an integer or float.
        :rtype: int | float
        '''
        
        # Check if the value is a valid number.
        is_valid = isinstance(value, str) and (value.isdigit() or (value.replace('.', '', 1).isdigit() and value.count('.') < 2))

        # Verify the value.
        self.verify(
            is_valid,
            'INVALID_INPUT',
            f"Invalid number: {value}",
            value
        )

        # If valid, return the value as a float or int.
        if '.' in value:
            return float(value)
        return int(value)

The BasicCalcEvent extends Tiferet's DomainEvent class, providing a verify_number method that validates string inputs and returns them as int or float. It raises an INVALID_INPUT error for invalid inputs, ensuring all arithmetic domain events inherit robust validation.

Arithmetic Domain Events in events/calc.py

The arithmetic domain events are the heart of the calculator, delivering core mathematical operations: addition, subtraction, multiplication, division, and exponentiation. These domain events leverage Python's numeric capabilities, processing validated inputs to produce precise results.

Create app/events/calc.py with the following content:

# *** imports

# ** core
from typing import Any

# ** infra
from tiferet.events import *

# ** app
from .settings import BasicCalcEvent

# *** events

# ** event: add_number
class AddNumber(BasicCalcEvent):
    '''
    A domain event to perform addition of two numbers.
    '''
    def execute(self, a: Any, b: Any, **kwargs) -> int | float:
        '''
        Execute the addition event.

        :param a: A number representing the first operand.
        :type a: Any
        :param b: A number representing the second operand.
        :type b: Any
        :param kwargs: Additional keyword arguments.
        :type kwargs: dict
        :return: The sum of a and b.
        :rtype: int | float
        '''
        # Verify numeric inputs
        a_verified = self.verify_number(str(a))
        b_verified = self.verify_number(str(b))

        # Add verified values of a and b.
        result = a_verified + b_verified

        # Return the result.
        return result

# ** event: subtract_number
class SubtractNumber(BasicCalcEvent):
    '''
    A domain event to perform subtraction of two numbers.
    '''
    def execute(self, a: Any, b: Any, **kwargs) -> int | float:
        '''
        Execute the subtraction event.

        :param a: A number representing the first operand.
        :type a: Any
        :param b: A number representing the second operand.
        :type b: Any
        :param kwargs: Additional keyword arguments.
        :type kwargs: dict
        :return: The difference of a and b.
        :rtype: int | float
        '''
        # Verify numeric inputs
        a_verified = self.verify_number(str(a))
        b_verified = self.verify_number(str(b))

        # Subtract verified values of b from a.
        result = a_verified - b_verified

        # Return the result.
        return result

# ** event: multiply_number
class MultiplyNumber(BasicCalcEvent):
    '''
    A domain event to perform multiplication of two numbers.
    '''
    def execute(self, a: Any, b: Any, **kwargs) -> int | float:
        '''
        Execute the multiplication event.

        :param a: A number representing the first operand.
        :type a: Any
        :param b: A number representing the second operand.
        :type b: Any
        :param kwargs: Additional keyword arguments.
        :type kwargs: dict
        :return: The product of a and b.
        :rtype: int | float
        '''
        # Verify numeric inputs
        a_verified = self.verify_number(str(a))
        b_verified = self.verify_number(str(b))

        # Multiply the verified values of a and b.
        result = a_verified * b_verified

        # Return the result.
        return result

# ** event: divide_number
class DivideNumber(BasicCalcEvent):
    '''
    A domain event to perform division of two numbers.
    '''
    def execute(self, a: Any, b: Any, **kwargs) -> int | float:
        '''
        Execute the division event.

        :param a: A number representing the numerator.
        :type a: Any
        :param b: A number representing the denominator, must be non-zero.
        :type b: Any
        :param kwargs: Additional keyword arguments.
        :type kwargs: dict
        :return: The quotient of a and b.
        :rtype: int | float
        '''
        # Verify numeric inputs
        a_verified = self.verify_number(str(a))
        b_verified = self.verify_number(str(b))

        # Check if b is zero to avoid division by zero.
        self.verify(b_verified != 0, 'DIVISION_BY_ZERO')

        # Divide the verified values of a by b.
        result = a_verified / b_verified

        # Return the result.
        return result

# ** event: exponentiate_number
class ExponentiateNumber(BasicCalcEvent):
    '''
    A domain event to perform exponentiation of two numbers.
    '''
    def execute(self, a: Any, b: Any, **kwargs) -> int | float:
        '''
        Execute the exponentiation event.

        :param a: A number representing the base.
        :type a: Any
        :param b: A number representing the exponent.
        :type b: Any
        :param kwargs: Additional keyword arguments.
        :type kwargs: dict
        :return: The result of a raised to the power of b.
        :rtype: int | float
        '''
        # Verify numeric inputs
        a_verified = self.verify_number(str(a))
        b_verified = self.verify_number(str(b))

        # Exponentiate the verified value of a by b.
        result = a_verified ** b_verified

        # Return the result.
        return result

These domain events perform arithmetic operations on flexible input values, validated by BasicCalcEvent.verify_number to ensure they are valid integers or floats. The method converts string inputs to int or float before computation, returning the result as a numeric value. The DivideNumber event includes a check to prevent division by zero, raising a configured DIVISION_BY_ZERO error if needed.

Configuring the Calculator Application

With domain event classes defined, it's time to configure the Tiferet application to recognize and orchestrate them, enabling seamless user interaction through features and interfaces. This section guides you through setting up the application interface (app/configs/app.yml), defining domain event classes as container attributes (app/configs/container.yml), specifying error messages (app/configs/error.yml), and organizing features (app/configs/feature.yml). These configurations weave together Tiferet's dependency injection and feature-driven design, ensuring a robust and extensible calculator.

Configuring the App Interface in configs/app.yml

Define the calculator's user interface in app/configs/app.yml to specify the interface type and its core attributes. This configuration enables Tiferet to initialize the application with default settings, supporting multiple interfaces for flexible feature execution.

Create app/configs/app.yml with the following content:

interfaces:
  basic_calc:
    name: Basic Calculator
    description: Perform basic calculator operations

The interfaces section configures the basic_calc interface with a name and description, using Tiferet's default settings to streamline application setup. This setup aligns with the project structure, preparing the calculator for domain event and feature integration.

Configuring the Container in configs/container.yml

Expose domain event classes to Tiferet by defining them as container attributes in app/configs/container.yml. This configuration maps each domain event to its module and class, enabling dependency injection for seamless feature execution.

Create the app/configs/container.yml with the following content:

attrs:
  add_number_event:
    module_path: app.events.calc
    class_name: AddNumber
  subtract_number_event:
    module_path: app.events.calc
    class_name: SubtractNumber
  multiply_number_event:
    module_path: app.events.calc
    class_name: MultiplyNumber
  divide_number_event:
    module_path: app.events.calc
    class_name: DivideNumber
  exponentiate_number_event:
    module_path: app.events.calc
    class_name: ExponentiateNumber

The attrs section lists each domain event with a unique identifier (ending in _event), specifying its module path and class name. This setup ensures Tiferet can instantiate and execute the calculator's arithmetic domain events efficiently.

Configuring the Errors in configs/error.yml

Handle errors gracefully by defining error messages in app/configs/error.yml. This configuration specifies error codes and multilingual messages, ensuring clear feedback for invalid inputs or operations.

Create app/configs/error.yml with the following content:

errors:
  invalid_input:
    name: Invalid Numeric Input
    message:
      - lang: en_US
        text: 'Value {} must be a number'
      - lang: es_ES
        text: 'El valor {} debe ser un número'
  division_by_zero:
    name: Division By Zero
    message:
      - lang: en_US
        text: 'Cannot divide by zero'
      - lang: es_ES
        text: 'No se puede dividir por cero'

The errors section defines invalid_input for failed numeric validations and division_by_zero for division errors, supporting English (en_US) and Spanish (es_ES) messages. This configuration enhances user experience with localized, precise error handling.

Configuring the Features in configs/feature.yml

Orchestrate domain event execution by defining features in app/configs/feature.yml. This configuration organizes arithmetic operations into reusable workflows, mapping each feature to its corresponding domain event for streamlined execution.

Create app/configs/feature.yml with the following content:

features:
  calc:
    add:
      name: 'Add Number'
      description: 'Adds one number to another'
      commands:
        - attribute_id: add_number_event
          name: Add `a` and `b`
    subtract:
      name: 'Subtract Number'
      description: 'Subtracts one number from another'
      commands:
        - attribute_id: subtract_number_event
          name: Subtract `b` from `a`
    multiply:
      name: 'Multiply Number'
      description: 'Multiplies one number by another'
      commands:
        - attribute_id: multiply_number_event
          name: Multiply `a` and `b`
    divide:
      name: 'Divide Number'
      description: 'Divides one number by another'
      commands:
        - attribute_id: divide_number_event
          name: Divide `a` by `b`
    exp:
      name: 'Exponentiate Number'
      description: 'Raises one number to the power of another'
      commands:
        - attribute_id: exponentiate_number_event
          name: Raise `a` to the power of `b`
    sqrt:
      name: 'Square Root'
      description: 'Calculates the square root of a number'
      commands:
        - attribute_id: exponentiate_number_event
          name: Calculate square root of `a`
          params:
            b: '0.5'  # Square root is equivalent to raising to the power of 0.5

The features section maps each operation (e.g., calc.add, calc.sqrt) to its domain event via attribute_id, with descriptive names and parameters. The calc.sqrt feature reuses exponentiate_number_event with a fixed b value of 0.5, showcasing Tiferet's flexible workflow design.

Initializing and Demonstrating the Calculator in basic_calc.py

Bring the Tiferet calculator to life with basic_calc.py, a script that initializes the application and demonstrates its arithmetic prowess. This script leverages Tiferet’s App class to load the basic_calc interface and execute features, showcasing robust calculations and error handling.

Create basic_calc.py with the following content:

from tiferet import App, TiferetError

# Initialize the Tiferet application with configuration settings.
app = App()

# Define test cases for calculator features.
test_cases = [
    ('calc.add', dict(a=1, b=2), '{} + {} = {}'),
    ('calc.subtract', dict(a=5, b=3), '{} - {} = {}'),
    ('calc.multiply', dict(a=4, b=3), '{} * {} = {}'),
    ('calc.divide', dict(a=8, b=2), '{} / {} = {}'),
    ('calc.divide', dict(a=8, b=0), '{} / {} = {}'),  # Expect error
    ('calc.exp', dict(a=2, b=3), '{} ** {} = {}'),
    ('calc.sqrt', dict(a=16), '√{} = {}')
]

# Execute each test case, demonstrating calculator features.
for feature_id, data, format_str in test_cases:
    a, b = data.get('a'), data.get('b', None)
    try:
        result = app.run('basic_calc', feature_id, data=data)
        print(format_str.format(a, b if b is not None else a, result))
    except TiferetError as e:
        print(f'Error: {e.message}')

The basic_calc.py script initializes the Tiferet application with settings from app/configs/app.yml, executes a series of test cases for arithmetic features, and handles errors gracefully. It demonstrates the calculator’s core functionality, from addition to square root, with clear output formatting.

Demonstrating the Calculator

Run the calculator to see its features in action, ensuring Tiferet’s power is fully unleashed. Activate your tiferet_app virtual environment and confirm Tiferet is installed, then execute the script to observe the results.

Execute the following command:

python basic_calc.py

This command runs basic_calc.py, producing output for each arithmetic operation and error case, leveraging configurations from app/configs/ to deliver precise, user-friendly results.

The Calculator as a CLI App

Unleash the calculator’s full potential with a dynamic command-line interface (CLI) powered by Tiferet’s CliContext. Implemented in calc_cli.py at the project root, this script offers a scriptable, user-friendly interface that complements the basic_calc.py test script for debugging. Leveraging Tiferet’s App class and CliContext, it executes features defined in app/configs/feature.yml, accepting command-line arguments for operations like addition (calc.add), subtraction (calc.subtract), multiplication (calc.multiply), division (calc.divide), exponentiation (calc.exp), and square root (calc.sqrt). This CLI interface seamlessly integrates with shell scripts and external systems, showcasing Tiferet’s flexibility in runtime environments.

Configure CLI Commands in configs/cli.yml

Define the CLI’s command structure in app/configs/cli.yml to enable CliContext to parse user inputs. This configuration specifies commands, their arguments, and descriptions, ensuring intuitive interaction with the calculator’s features.

Create app/configs/cli.yml with the following content:

cli:
  cmds:
    calc:
      add:
        group_key: calc
        key: add
        description: Adds two numbers.
        args:
          - name_or_flags:
              - a
            description: The first number to add.
          - name_or_flags:
              - b
            description: The second number to add.
        name: Add Number Command
      subtract:
        group_key: calc
        key: subtract
        description: Subtracts one number from another.
        args:
          - name_or_flags:
              - a
            description: The number to subtract from.
          - name_or_flags:
              - b
            description: The number to subtract.
        name: Subtract Number Command
      multiply:
        group_key: calc
        key: multiply
        description: Multiplies two numbers.
        args:
          - name_or_flags:
              - a
            description: The first number to multiply.
          - name_or_flags:
              - b
            description: The second number to multiply.
        name: Multiply Number Command
      divide:
        group_key: calc
        key: divide
        description: Divides one number by another.
        args:
          - name_or_flags:
              - a
            description: The numerator.
          - name_or_flags:
              - b
            description: The denominator.
        name: Divide Number Command
      sqrt:
        group_key: calc
        key: sqrt
        description: Calculates the square root of a number.
        args:
          - name_or_flags:
              - a
            description: The number to square root.
        name: Square Root Command

The cli.cmds section maps each feature (e.g., calc.add) to its command group (calc), key (e.g., add), and arguments (a, b), enabling CliContext to parse inputs and execute features with precision.

Configuring the CLI Interface in configs/app.yml

Enable the CLI interface by adding the calc_cli interface to app/configs/app.yml. This configuration integrates CliContext with its dependencies, CliYamlProxy and CliHandler, to manage command-line interactions.

Add the calc_cli interface to the interfaces section:

interfaces:
  basic_calc:
    name: Basic Calculator
    description: Perform basic calculator operations
  calc_cli:
    name: Calculator CLI
    description: Perform basic calculator operations via CLI
    module_path: tiferet.contexts.cli
    class_name: CliContext
    attrs:
      cli_repo:
        module_path: tiferet.proxies.yaml.cli
        class_name: CliYamlProxy
        params:
          cli_config_file: app/configs/cli.yml
      cli_service:
        module_path: tiferet.handlers.cli
        class_name: CliHandler

The calc_cli interface specifies CliContext as its implementation, with cli_repo and cli_service attributes linking to CliYamlProxy (loading cli.yml) and CliHandler for command processing. This setup ensures robust CLI functionality within Tiferet’s ecosystem.

Creating the CLI Script

Craft a streamlined CLI script in calc_cli.py to initialize and run the calculator’s command-line interface. This script uses Tiferet’s App class to load the calc_cli interface, powered by CliContext, for seamless command execution.

Create calc_cli.py with the following content:

from tiferet import App

# Create new app (manager) instance.
app = App()

# Load the CLI app instance.
cli = app.load_interface('calc_cli')

# Run the CLI app.
if __name__ == '__main__':
    cli.run()

The calc_cli.py script initializes the App, loads the calc_cli interface, and executes commands via CliContext, with error handling to ensure user-friendly feedback for invalid inputs or operations.

Demonstrating the CLI Calculator

Experience the calculator’s CLI in action by running commands that showcase its arithmetic and error-handling capabilities. Ensure the tiferet_app virtual environment is activated and Tiferet is installed, then execute the script with feature-specific arguments.

Run the CLI with commands like:

# Add two numbers
python calc_cli.py calc add 1 2
# Output: 3

# Calculate square root
python calc_cli.py calc sqrt 4
# Output: 2.0

# Division by zero
python calc_cli.py calc divide 5 0
# Output: Error: Cannot divide by zero

The calc_cli.py script offers a versatile, scriptable interface that integrates effortlessly with shell scripts or external systems, powered by CliContext for robust command execution. For quick testing or debugging, use basic_calc.py to run individual features with predefined values, while the CLI provides precise, argument-driven control.

Documentation

Tiferet provides detailed documentation for framework internals and utility usage.

Core Design Documents

Architectural references for framework contributors and advanced users:

  • Code Style — Artifact comments, spacing, docstrings, and formatting conventions
  • Domain Objects — Domain model structure and factory patterns
  • Domain Events — Event patterns, validation, and testing
  • Interfaces — Service contract conventions
  • Mappers — Aggregate and TransferObject patterns
  • Utilities — Infrastructure utilities design and best practices

Domain Guides

Practical guides for working with Tiferet's domain layers:

Utility Guides

Practical guides for using Tiferet's built-in utilities:

Conclusion

Embark on a journey of elegance and precision with Tiferet's Domain-Driven Design framework, as this tutorial has crafted a robust calculator application. You've defined arithmetic and validation domain events in app/events/calc.py and app/events/settings.py, orchestrated them with configurations in app/configs/app.yml, app/configs/feature.yml, and app/configs/cli.yml, and brought them to life through basic_calc.py and the CliContext-powered calc_cli.py. This configuration-driven approach, enriched with dependency injection and multilingual error handling, reflects Tiferet's harmonious balance of clarity and power, making development both functional and delightful.

Extend your calculator's potential with Tiferet's modular design. Create a terminal user interface (TUI) in calc_tui.py, leveraging CliContext for interactive operation, or define a scientific calculator interface (sci_calc) in app/configs/app.yml with advanced features like trigonometric functions. Integrate the calculator into larger systems, such as financial modeling, by adding new domain events and features in app/configs/feature.yml. Experiment with calc_cli.py to test additional operations, tweak configurations in app/configs/, or explore Tiferet's documentation for advanced DDD techniques. Let Tiferet's graceful framework guide you to solutions that resonate with purpose and precision, transforming complexity into clarity.

Project details


Release history Release notifications | RSS feed

Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

tiferet-2.0.0a4.tar.gz (133.4 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

tiferet-2.0.0a4-py3-none-any.whl (194.0 kB view details)

Uploaded Python 3

File details

Details for the file tiferet-2.0.0a4.tar.gz.

File metadata

  • Download URL: tiferet-2.0.0a4.tar.gz
  • Upload date:
  • Size: 133.4 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.7

File hashes

Hashes for tiferet-2.0.0a4.tar.gz
Algorithm Hash digest
SHA256 3aca33fc9df67fac850f011a15fdf7f83fa7043e80ba5a9b4b6ff6fc1b69025d
MD5 8d1058e01be3d2e1c5761edd0aa7569d
BLAKE2b-256 8a66f6b24c29b3555eb2bf604b41c746a5019fe314332b3eb1e22e149b30f81d

See more details on using hashes here.

Provenance

The following attestation bundles were made for tiferet-2.0.0a4.tar.gz:

Publisher: python-publish.yml on greatstrength/tiferet

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file tiferet-2.0.0a4-py3-none-any.whl.

File metadata

  • Download URL: tiferet-2.0.0a4-py3-none-any.whl
  • Upload date:
  • Size: 194.0 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.7

File hashes

Hashes for tiferet-2.0.0a4-py3-none-any.whl
Algorithm Hash digest
SHA256 5dc45c042d679d97ea704b23037797c30960d2f0a1099024fd088b5c1cb9fd77
MD5 ddd73d91c1b89838ea41545e741a1420
BLAKE2b-256 c5a76e00027716a48f50c1dc13041a5b8dc895c287d6549a925f3bcbd501a7a0

See more details on using hashes here.

Provenance

The following attestation bundles were made for tiferet-2.0.0a4-py3-none-any.whl:

Publisher: python-publish.yml on greatstrength/tiferet

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page