springcalc
A Python library for spring calculations: compression, extension, and torsion springs. Includes material data, wire characteristic calculations, and fatigue analysis via the Goodman diagram.
Visit the library in my guithub https://github.com/ErnestoAvedillo/springcalc and clone my repository using:
git clone git@github.com:ErnestoAvedillo/springcalc.git
Structure
src/springcalc/ Library package
├── lineal/ Calculation engine (compression, extension, torsion, Goodman,
│ 3D visualization, progressive-compression animation)
├── pymodels/ Data models (pydantic): material, units, wire, positions
├── material/ Material and tolerance tables (CSV, package data)
├── regresiones/ Fitted models loaded at runtime
│ └── factor_f/ Shigley's factor f (plain JSON coefficients + loader)
├── plots/ Goodman diagram generation
└── report/ PDF report generation (SpringPDFReport)
tests/ Tests (pytest)
scripts/regresiones/ Training scripts that regenerate the JSON coefficients (not runtime)
docs/ Reference material (spreadsheet, figures)
Installation
I recommend to use uv to install the library (https://docs.astral.sh/uv/):
# create the environment and install dependencies
uv init
uv add springcalc
Usage
from springcalc import Material, CompressionSpring
material = Material(material_name="SH")
spring = CompressionSpring(material=material, wire_diameter=1.0)
spring.set_geometry(outer_diameter=10.0, free_length=50.0, nr_coils=10)
properties = compression_spring.get_spring_data()
for key, value in properties.items():
print(f"{key}: {value}") )
Generating a PDF report (spring data, load/travel/diameter curves, and the Goodman fatigue diagram):
from springcalc.report import SpringPDFReport
report = SpringPDFReport(spring, title="Spring XYZ-123")
report.build("spring_report.pdf")
API Reference
- Material data —
Material,get_available_materials() - Wire characteristics —
WireCharacteristics - Compression springs —
CompressionSpring - Extension springs —
ExtensionSpring - Torsion springs —
TorsionSpring - Fatigue analysis (Goodman diagram) —
GoodmanData,GoodmanAnalyzer,Goodman,generate_goodman_diagram() - Position tables —
LinearPositionsTable,AngularPositionsTable - PDF reports —
SpringPDFReport - Advanced: variable-geometry springs —
VariableLinealSpring,CompressionSpringGeneral - Animating progressive compression —
CompressionAnimator
All physical quantities are pint Quantity
objects (a number with a unit, e.g. 20.0 millimeter). Plain numbers passed
into a field are usually interpreted in that field's default unit (mm, N,
degrees...), but a few methods require an explicit Quantity — those are
called out below. Import the shared unit registry with:
from springcalc.pymodels.units import ureg
length = 20 * ureg.mm
angle = 90 * ureg.degree
Material data
Material (springcalc.pymodels.material.Material) is a pydantic model that
looks up a named material's mechanical properties from material/materials.csv
and auto-fills any field you don't pass explicitly.
| Member | Description |
|---|---|
Material(material_name, young_modulus=None, shear_modulus=None, elastic_limit_factor=None, poisson_coef=None, RMa_file=None) |
Construct a material by name; unset fields are auto-filled from materials.csv. |
.young_modulus |
Young's modulus E, as a Quantity in MPa. |
.shear_modulus |
Shear modulus G, as a Quantity in MPa. |
.elastic_limit_factor |
Dimensionless factor used to derive the fatigue limit. |
.poisson_coef |
Poisson's ratio (dimensionless). |
.RMa_file |
Name of the CSV (in material/) with tensile-strength ranges by wire diameter. |
get_available_materials() |
Module-level function: list of valid material_name values. |
Material.list_available_materials() |
Classmethod: list of valid material_name values (same as get_available_materials()). |
Material.material_exists(material_name) |
Classmethod: whether a material is already registered. |
Material.create_material(material_name, young_modulus, shear_modulus, elastic_limit_factor, poisson_coef, description="", RMa_file=None, RMa_data=None, overwrite=False) |
Classmethod: register a new material in materials.csv (and optionally its RMa_file table) and return it. Raises ValueError if the name already exists, unless overwrite=True. |
from springcalc import Material, get_available_materials
print(get_available_materials())
# ['SL', 'SM', 'DM', 'SH', 'DH', 'TDC', 'TDCrV', ...]
material = Material(material_name="SH")
print(material.young_modulus) # 206000.0 megapascal
print(material.shear_modulus) # 81500.0 megapascal
print(material.poisson_coef) # 0.3 dimensionless
Adding a new material — young_modulus/shear_modulus accept a unit string, a
plain number (assumed MPa), or a Quantity; elastic_limit_factor and
poisson_coef are dimensionless. Once created, the material is loadable by
name like any built-in one:
from springcalc import Material
custom = Material.create_material(
material_name="CustomSteel",
young_modulus="210000 MPa",
shear_modulus=81000,
elastic_limit_factor=0.5,
poisson_coef=0.3,
description="Custom steel for a special order",
RMa_data=[(1.0, 1600, 1800), (2.0, 1500, 1700)], # (diameter mm, RMa_min, RMa_max)
)
# From now on it behaves like any other material
same_material = Material(material_name="CustomSteel")
Wire characteristics
WireCharacteristics (springcalc.pymodels.wire_characteristics.WireCharacteristics)
is the base class of every spring type. Given a material and a wire diameter,
it looks up the wire diameter tolerance and the tensile-strength range (RMa)
for that diameter.
| Member | Description |
|---|---|
WireCharacteristics(material, wire_diameter) |
wire_diameter may be a plain number (interpreted in mm) or a Quantity. |
.diameter_tolerance |
Manufacturing tolerance for this diameter (from DIAMETRO_TOLERANCIAS.csv). |
.RMa_min / .RMa_max |
Tensile-strength range for this material and diameter, from material.RMa_file. |
.set_material(material, wire_diameter) |
Re-assign the material/diameter and refresh the derived fields. |
from springcalc import Material
from springcalc.pymodels.wire_characteristics import WireCharacteristics
material = Material(material_name="SH")
wire = WireCharacteristics(material=material, wire_diameter=2.0)
print(wire.diameter_tolerance) # 0.025
print(wire.RMa_min, wire.RMa_max) # 1980.0 2200.0
Compression springs
CompressionSpring (springcalc.lineal.compresion.CompressionSpring, also
exported as springcalc.CompressionSpring) is the main entry point for
helical compression springs. It extends LinealSpring
(springcalc.lineal.lineal.LinealSpring), the shared calculation engine also
used by ExtensionSpring.
| Method | Description |
|---|---|
CompressionSpring(material, wire_diameter, **data) |
Create the spring. |
.set_geometry(mean_diameter=None, outer_diameter=None, inner_diameter=None, nr_coils=None, pitch=None, free_length=None, type_of_end=None, type_conforming=None) |
Set the full geometry in one call: exactly one diameter and exactly two of nr_coils/pitch/free_length. type_of_end (one of constants.COMPRESSION_SPRING_END_TYPES, e.g. "open_ground") and type_conforming (one of constants.FORMING_TYPES, e.g. "cold_formed") are optional and, if given, override the spring's defaults before the active-coil count is computed. Equivalent to calling .set_diameter() followed by .calculate_spring_properties(). Returns .get_spring_data(). |
.set_diameter(mean_diameter=None, outer_diameter=None, inner_diameter=None) |
Set exactly one of the three diameters; derives the others and the spring index/Wahl factor. |
.calculate_spring_properties(nr_coils=None, pitch=None, free_length=None) |
Provide exactly two of the three; computes coils, active coils, Wahl factor, spring constant, solid length, and wire length. |
.add_load_position(length) |
Record the load/stress/outer-diameter at a given compressed length, for the load-position table and fatigue analysis. |
.empty_tables() |
Clear the recorded load positions. |
.get_spring_data() |
dict with all computed spring properties (material, diameters, constants, Wahl factor, etc.). |
.get_data_positions() / .get_data_travels() |
List of LinearLoadPosition recorded via add_load_position. |
.get_forces_vs_position_graph(show=False) |
Load vs. absolute position curve; returns a base64-encoded PNG. |
.get_forces_vs_travel_graph(show=False) |
Load vs. travel (compression from free length) curve; returns a base64 PNG. |
.get_diameter_graph(show=False) |
Outer diameter vs. position curve; returns a base64 PNG. |
.get_diameter_vs_position_graph(show=False) |
Outer diameter curve plus a to-scale cross-section diagram; returns a base64 PNG. |
.get_3d_plot(num_points=200, show=False, isometric=True) |
Renders the coiled wire geometry in 3D (constant mean diameter and pitch); returns a base64 PNG. Defaults to an orthographic isometric view, matching how spring drawings are conventionally presented. |
.create_goodman_diagram(show=False) |
Runs the fatigue (Goodman) analysis from the recorded positions; returns {"image", "analysis", "stresses"} or {"error", "traceback"}. |
.get_stress_max() / .get_stress_min() |
Max/min stress across recorded positions (raises if none are recorded). |
.get_load_max() / .get_load_min() |
Max/min load across recorded positions. |
.calculate_solid_length() |
Coils-stacked solid (fully compressed) length. |
.calculate_wire_length() |
Total wire length needed to wind the spring. |
.set_number_cycles(number_cycles) |
Design life, in cycles, used by the fatigue analysis (default 1e6). |
from springcalc import Material, CompressionSpring
material = Material(material_name="SL")
spring = CompressionSpring(material=material, wire_diameter=2.5)
spring.set_geometry(outer_diameter=30, pitch=20, free_length=100) # mm
# Equivalent to calling separately:
# spring.set_diameter(outer_diameter=30)
# spring.calculate_spring_properties(pitch=20, free_length=100)
for length_mm in [30, 40, 50, 60, 70, 80, 90, 100]:
spring.add_load_position(length=length_mm)
data = spring.get_spring_data()
print(data["spring_constant"]) # ~7.09 N / mm
print(data["wahl_factor_category"]) # 'green' -> C=11 is in a normal manufacturable range
# Graphs (base64 PNGs, ready to embed in HTML or a PDF)
load_vs_position_png = spring.get_forces_vs_position_graph()
diameter_png = spring.get_diameter_vs_position_graph()
# Fatigue analysis from the recorded positions
result = spring.create_goodman_diagram()
print(result["analysis"]["safety_factor"])
Extension springs
ExtensionSpring (springcalc.lineal.extension.ExtensionSpring, also exported
as springcalc.ExtensionSpring) models helical extension springs. It extends
LinealSpring directly (not CompressionSpring) and its diameter/length
setters require explicit Quantity values rather than plain numbers.
| Method | Description |
|---|---|
ExtensionSpring(material, wire_diameter, **data) |
Create the spring. |
.set_diameter(outer_diameter=None, inner_diameter=None, mean_diameter=None) |
Set exactly one diameter. Must be a Quantity (e.g. 15 * ureg.mm), not a plain number. |
.calculate_spring_properties(nr_coils=None, pitch=None, free_length=None) |
Provide exactly two of the three; computes active coils, spring constant, and wire length. |
.add_load_position(length) |
Record the load/stress/outer-diameter at a given extended length (must be ≥ free length). |
.calculate_positions_table(step: list) |
Convenience: call add_load_position for each length in step. |
.empty_tables() |
Clear the recorded load positions. |
.get_spring_data() |
dict with all computed spring properties. |
.get_data_positions() / .get_data_travels() |
List of recorded LinearLoadPosition. |
.get_forces_vs_position_graph(show=False) / .get_forces_vs_travel_graph(show=False) |
Load curves; return base64 PNGs. |
.get_diameter_graph() / .get_diameter_vs_position_graph() |
Diameter curves (no show parameter on this class); return base64 PNGs. |
.create_goodman_diagram() |
Fatigue analysis from the recorded positions; returns {"image", "analysis", "stresses"} or {"error", "traceback"} (no show parameter). |
.get_stress_max() / .get_stress_min() / .get_load_max() / .get_load_min() |
Extremes across recorded positions. |
.set_number_cycles(number_cycles) |
Design life in cycles for the fatigue analysis. |
.set_initial_stress(initial_stress) |
Set the spring's initial tension (pre-load) stress. |
from springcalc import Material, ExtensionSpring
from springcalc.pymodels.units import ureg
material = Material(material_name="SH")
spring = ExtensionSpring(material=material, wire_diameter=1.5)
spring.set_diameter(outer_diameter=15 * ureg.mm) # note: needs a Quantity, unlike CompressionSpring
spring.calculate_spring_properties(nr_coils=10, free_length=60)
spring.calculate_positions_table([65, 70, 75, 80]) # extend beyond the free length
data = spring.get_spring_data()
print(data["spring_constant"]) # ~2.33 N / mm
result = spring.create_goodman_diagram()
print(result["analysis"]["safety_factor"])
Torsion springs
TorsionSpring (springcalc.lineal.torsion.TorsionSpring, also exported as
springcalc.TorsionSpring) models helical torsion springs, tracking angular
position/travel and torque instead of linear load. It extends
WireCharacteristics directly and has no Goodman/fatigue integration.
| Method | Description |
|---|---|
TorsionSpring(material, wire_diameter, **data) |
Create the spring. |
.set_geometry(mean_diameter, nr_coils, pitch, free_angle, fixed_leg_radius, mobile_leg_radius) |
One-call setup: sets geometry and computes every derived property. Returns .get_spring_properties(). |
.calculate_spring_properties() |
Re-run the derived-property calculations after changing an input. |
.add_position(angle_travel=None, torque=None) |
Record a working position from either an angular travel or a torque (exactly one). |
.clean_positions() |
Clear the recorded positions. |
.get_positions() / .get_data_positions() / .get_data_travels() |
List of recorded AngularLoadPosition. |
.get_spring_properties() |
dict with all computed properties (diameters, angles, leg lengths, spring constant, Wahl factor, etc.). |
.calculate_torque(rotation_angle) |
Torque required for a given rotation angle. |
.calculate_stress(torque) |
Max wire stress for a given torque. |
.get_forces_vs_position_graph(show=False) / .get_forces_vs_travel_graph(show=False) |
Torque vs. angular position/travel curves; return base64 PNGs. |
.get_diameter_vs_position_graph(show=False) |
Outer diameter curve plus a cross-section diagram; returns a base64 PNG. |
.set_number_cycles(number_cycles) / .set_shot_peening(shot_peening) |
Fatigue-related inputs (stored but not yet used by a Goodman analysis for this class). |
from springcalc import Material, TorsionSpring
from springcalc.pymodels.units import ureg
material = Material(material_name="SH")
spring = TorsionSpring(material=material, wire_diameter=1.0)
spring.set_geometry(
mean_diameter=10 * ureg.mm,
nr_coils=8,
pitch=1.2 * ureg.mm,
free_angle=180 * ureg.degree,
fixed_leg_radius=15 * ureg.mm,
mobile_leg_radius=15 * ureg.mm,
)
print(spring.spring_constant) # ~35 mm*N/rad
spring.add_position(angle_travel=30 * ureg.degree)
for position in spring.get_positions():
print(position)
Fatigue analysis (Goodman diagram)
springcalc.lineal.goodman implements the modified-Goodman fatigue check
(Shigley, ch. 6/10) for spring wire in torsion, axial, or flexural loading.
CompressionSpring.create_goodman_diagram() and
ExtensionSpring.create_goodman_diagram() use this internally, but it can
also be used directly.
| Member | Description |
|---|---|
GoodmanData(material, diameter, load_type="axial", cycles=1e6) |
Pydantic input model. load_type is "axial", "torsion", or "flexion". |
GoodmanAnalyzer(data, shot_peening=False) |
Computes the Marin correction factors, the corrected endurance limit Sse and fatigue strength Ssf. |
.calculate_safety_factor(sigma_max, sigma_min) |
Modified-Goodman safety factor for an operating stress cycle. |
.get_analysis_summary(sigma_max, sigma_min) |
dict with the correction factors, strengths, operating point, and safety factor. |
.plot_diagram(sigma_max, sigma_min, show_plot=True) |
Returns a matplotlib Figure with the Goodman envelope and the operating point plotted. |
.get_diagram_image(sigma_max, sigma_min) |
Same diagram, returned as a base64 PNG string. |
Goodman(material, diameter, load_type="axial", number_cycles=1e6, shot_peening=False) |
Backwards-compatible wrapper around GoodmanAnalyzer with the same methods (plot_goodman_graph, get_goodman_graph, etc.). |
generate_goodman_diagram(spring, initial_length, final_length, shot_peening=False, number_cycles=1e6) |
Module function in springcalc.plots: derives max/min load and stress for a spring compressed between two lengths, and returns the same {"image", "analysis", "stresses"} dict. initial_length/final_length must be Quantity values (they're subtracted directly from spring.free_length). |
from springcalc import Material, GoodmanData, GoodmanAnalyzer
material = Material(material_name="DH")
data = GoodmanData(material=material, diameter=1.0, load_type="torsion", cycles=1e5)
analyzer = GoodmanAnalyzer(data)
sigma_max, sigma_min = 400, 100 # MPa
print(analyzer.calculate_safety_factor(sigma_max, sigma_min))
summary = analyzer.get_analysis_summary(sigma_max, sigma_min)
print(summary["strengths"]["Se_MPa"], summary["strengths"]["Sf_MPa"])
Using the standalone helper directly on a CompressionSpring instance (see
the Compression springs example for how spring was
built), instead of calling spring.create_goodman_diagram():
from springcalc.plots import generate_goodman_diagram
from springcalc.pymodels.units import ureg
result = generate_goodman_diagram(spring, initial_length=100 * ureg.mm, final_length=40 * ureg.mm)
print(result["analysis"]["safety_factor"])
Position tables
Every spring stores its recorded working positions in a pydantic list model
under .positions, populated via each spring's add_load_position/
add_position method rather than built by hand — but they're documented here
since .get_data_positions() returns their contents.
| Model | Used by | Fields |
|---|---|---|
LinearLoadPosition (springcalc.pymodels.positions) |
CompressionSpring, ExtensionSpring |
.position, .travel, .load (N), .stress (MPa), .outer_diameter, .inner_diameter — all Quantity. |
LinearPositionsTable |
same | .positions: list[LinearLoadPosition]. .add_load_position(...), .clear_table(). |
AngularLoadPosition |
TorsionSpring |
Same fields as above, with .position/.travel in degrees and .load in N·mm (torque). |
AngularPositionsTable |
TorsionSpring |
.positions: list[AngularLoadPosition]. .add_load_position(...), .clear_table(). |
# `spring` is any CompressionSpring/ExtensionSpring/TorsionSpring instance
# with load positions already recorded via add_load_position/add_position.
for position in spring.get_data_positions():
print(position.position, position.load, position.stress)
PDF reports
SpringPDFReport (springcalc.report.SpringPDFReport, also exported as
springcalc.SpringPDFReport) renders a CompressionSpring's data, a 3D
isometric view, curves, and the Goodman diagram into a printable PDF using
reportlab.
| Method | Description |
|---|---|
SpringPDFReport(spring, title=None) |
Wrap a CompressionSpring (with load positions already added via add_load_position, for the fullest report). |
.build(output_path) |
Render the report and write it to output_path. Returns the path. Includes the spring data table, a 3D isometric view (from spring.get_3d_plot()), the load/geometry curves, the load-position table, and the Goodman diagram. Degrades gracefully (with a placeholder message) if a graph or the Goodman analysis can't be generated, e.g. no load positions recorded yet. |
from springcalc.report import SpringPDFReport
# `spring` is a CompressionSpring instance (see the Compression springs example)
report = SpringPDFReport(spring, title="Spring XYZ-123")
report.build("spring_report.pdf")
Advanced: variable-geometry springs
VariableLinealSpring and CompressionSpringGeneral
(springcalc.lineal.generic_lineal / springcalc.lineal.generic_compression)
model compression springs whose mean diameter and/or pitch vary along their
length (e.g. conical or barrel springs), by numerically integrating along the
helix instead of using the constant-geometry closed-form equations. They are
not exported from the top-level springcalc package — import them from their
modules directly. For a constant-diameter, constant-pitch spring they agree
with the closed-form CompressionSpring results.
| Method | Description |
|---|---|
CompressionSpringGeneral(material, wire_diameter, **data) |
Create the spring. Set .mean_diameter_init, .pitch_constant, and .free_length for a constant-geometry spring, or... |
.establish_geometrical_function(func_D, func_p) |
...inject custom functions h -> mean_diameter and h -> pitch (both Quantity -> Quantity) for a true variable-geometry spring. |
.set_geometry(func_D, func_p, free_length=None, type_of_end=None, type_conforming=None) |
One-call setup: calls .establish_geometrical_function(func_D, func_p), sets .free_length, and optionally type_of_end (one of constants.COMPRESSION_SPRING_END_TYPES, e.g. "open_ground") and type_conforming (one of constants.FORMING_TYPES, e.g. "cold_formed") — both default to the spring's current value when omitted. |
.calculate_theta_max() |
Total helix rotation angle (rad) needed to reach free_length; also updates .nr_coils. |
.calculate_active_coils() |
Number of active coils (.nr_active_coils), discounting the ground/squared end coils that don't deform, based on type_of_end/type_conforming — same formula as CompressionSpring. |
.calculate_spring_constant(num_points=500) |
Equivalent stiffness, integrating the local flexibility along the helix over the active coils only (end coils excluded per .calculate_active_coils()). |
.calculate_wire_length(num_points=500) |
Total wire length, integrating the 3D arc length along the helix. |
.calculate_solid_length() |
Solid (fully compressed) length, accounting for coil telescoping/nesting when the diameter varies enough. |
.get_3d_plot(num_points=500, show=False, isometric=True) |
Renders the helix centerline in 3D, following the actual f_mean_diameter/f_pitch functions (so variable geometries show up as a non-uniform helix); returns a base64 PNG. Defaults to an orthographic isometric view. |
.simulate_progressive_compression(max_deflection, steps=100, num_points=500, capture_geometry=False) |
Step-by-step compression simulation that detects coil-to-coil (oblique) contact; returns (deflection, force, instantaneous_stiffness) arrays. With capture_geometry=True, also returns a 4th value: {"thetas", "z_history"}, the instantaneous coil shape at every step (used by CompressionAnimator, see below). |
from springcalc import Material
from springcalc.pymodels.units import ureg
from springcalc.lineal.generic_compression import CompressionSpringGeneral
material = Material(material_name="SH")
spring = CompressionSpringGeneral(material=material, wire_diameter=2.0)
spring.set_geometry(
func_D=lambda h: 20 * ureg.mm,
func_p=lambda h: 6 * ureg.mm,
free_length=60 * ureg.mm,
type_of_end="open_ground", # optional; this is the default
type_conforming="cold_formed", # optional; this is the default
)
spring.calculate_theta_max()
print(spring.calculate_spring_constant()) # matches G*d^4/(8*D^3*n_active) for constant geometry
print(spring.nr_active_coils) # 7.7 (10 total coils minus the non-deforming ground ends)
deflection, force, stiffness = spring.simulate_progressive_compression(max_deflection=20 * ureg.mm, steps=20)
print(force[-1]) # ~52.92 N
Animating progressive compression
CompressionAnimator (springcalc.lineal.animation.CompressionAnimator) renders
simulate_progressive_compression's result as an animated GIF of the coils
closing up under load, reusing the same helix geometry as get_3d_plot. It
takes any VariableLinealSpring (e.g. CompressionSpringGeneral). Saving is
done with matplotlib's PillowWriter, so no extra system dependency (like
ffmpeg) is required.
| Method | Description |
|---|---|
CompressionAnimator(spring) |
Wrap a CompressionSpringGeneral (or other VariableLinealSpring) instance. |
.create_gif(max_deflection, output_path="compression.gif", steps=60, num_points=300, fps=12, isometric=True) |
Runs simulate_progressive_compression(capture_geometry=True) internally and writes the resulting animation to output_path. Returns output_path. Axis limits are fixed from the free-state geometry so the camera doesn't jump between frames. |
from springcalc import Material
from springcalc.pymodels.units import ureg
from springcalc.lineal.generic_compression import CompressionSpringGeneral
from springcalc.lineal.animation import CompressionAnimator
material = Material(material_name="SH")
spring = CompressionSpringGeneral(material=material, wire_diameter=2.0)
spring.set_geometry(func_D=lambda h: 20 * ureg.mm, func_p=lambda h: 6 * ureg.mm, free_length=60 * ureg.mm)
animator = CompressionAnimator(spring)
animator.create_gif(max_deflection=25 * ureg.mm, output_path="compression.gif")
Tests
uv run pytest
Retraining the regression models
The included JSON coefficient files are already fitted. To regenerate them:
uv run python scripts/regresiones/factor_f/factor_f.py
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file springcalc-0.1.17.tar.gz.
File metadata
- Download URL: springcalc-0.1.17.tar.gz
- Upload date:
- Size: 2.4 MB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via:
twine/7.0.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
7f0e8e250e48097ad03d98f7d3e3bb5e6c76aed24ddee4c7dc51413a70c96170
|
|
| MD5 |
35089b5de9ff233c6c7bff6abd9832fa
|
|
| BLAKE2b-256 |
e8195432082f13c93092e225eff9ce6f1793d42021d83a33e383020ff5cc163f
|
File details
Details for the file springcalc-0.1.17-py3-none-any.whl.
File metadata
- Download URL: springcalc-0.1.17-py3-none-any.whl
- Upload date:
- Size: 65.0 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via:
twine/7.0.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
6e076b9057f89d4bfc0fc2613bf7a7238b620feaa439f83754c6658f62405daa
|
|
| MD5 |
f310c2ef1daaea9d596a696b1b37aa55
|
|
| BLAKE2b-256 |
c3218118f5ecb9d615584e739e89112823d2e09350b7e775805e478956176bf0
|