This release is a pre-release and may not be stable for production use.
Galaga — Geometric Algebra for Python
Galaga 2 is a numeric geometric-algebra library for calculations and teaching. Build an algebra from a signature or a full Gram matrix, compute with immutable multivectors, and display the same calculation as values, named expressions, or LaTeX. Expression tracking records how an eagerly computed value was obtained; it is not a deferred symbolic algebra system.
- Real symmetric metrics, including nonorthogonal, indefinite and degenerate algebras, with native-null conformal bases.
- Explicit geometric, exterior, inner and regressive products; checked inverses, exponentials, principal logarithms and rotor generators.
- Composable presets for metrics, blade vocabulary and operation notation.
- Shared ASCII, Unicode and LaTeX rendering, including labelled Gram and wedge product tables for notebooks.
- Optional conformal and rigid model APIs, with separate companion packages for matrices, notebook Markdown, visualization and expression diagrams.
Installation and status
Galaga 2 is an alpha release. This guide uses the APIs available in
2.0.0a4; opt into the v2 prerelease train explicitly:
python -m pip install --pre "galaga>=2.0.0a4,<3"
For reproducible notebooks, pin the version you tested, for example
galaga==2.0.0a4. The core package requires Python 3.11+ and NumPy.
It does not require Marimo, Jupyter or a matrix package.
The examples below are self-contained Python snippets. Only the
galaga_marimo t-string example requires Python 3.14 and an optional package.
Documentation links target the galaga_v2 branch while v2 is in development.
Existing v1 users should start with the migration guide.
Quick start
from galaga import Algebra, norm, outer_product, presets
alg = Algebra(config=presets.euclidean(3))
e1, e2, e3 = alg.basis_vectors(expr=True)
u = (2 * e1 + e2).named("u")
v = (e1 + 3 * e2).named("v")
area = outer_product(u, v).named("A")
print(area.display("value/unicode")) # 5e₁₂
print(area.display("expr/unicode")) # u ∧ v
print(area.display("full/latex")) # Name = expression = value, as LaTeX.
assert area == 5 * (e1 ^ e2)
assert float(norm(area)) == 5
The wedge is an oriented area bivector, not a cross-product vector. Names and expression history do not affect its numeric value.
Common operators are conveniences over named functions:
| Python | Named operation | Meaning |
|---|---|---|
a * b |
geometric_product(a, b) |
Clifford product |
a ^ b |
outer_product(a, b) |
Exterior product |
a | b |
doran_lasenby_inner(a, b) |
Grade-difference inner product, including scalars |
~a |
reverse(a) |
Reversion, not inversion |
a / b |
geometric_product(a, inverse(b)) |
Right division for multivector b |
Parenthesize wedge expressions when mixing them with other Python operators.
Long names are the primary API; gp, op, rev and sw remain concise aliases.
Define the metric
from galaga import Algebra, metric_inner_product
euclidean = Algebra(3) # Cl(3, 0)
spacetime = Algebra(1, 3) # Ordered squares: +1, -1, -1, -1.
projective = Algebra(3, 0, 1) # Three positive, then one null vector.
time_first = Algebra((-1, 1, 1, 1)) # Explicit ordered signature.
oblique = Algebra(gram=[[2.0, 1.0], [1.0, 3.0]])
e1, e2 = oblique.basis_vectors()
assert float(metric_inner_product(e1, e2)) == oblique.gram[0, 1]
assert e1 * e2 == oblique.gram[0, 1] + (e1 ^ e2)
The defining identity is $e_i e_j + e_j e_i = 2G_{ij}$. Use gram= for
scaled or nonorthogonal metrics; ordered signatures contain only +1, -1
and 0. Algebra(p, q, r) puts positive, negative, then null vectors in
that order. Thus Algebra(3, 1) is not the time-first mostly-plus frame
Algebra((-1, 1, 1, 1)).
Coefficients are real float64 values in the native exterior basis.
In a nonorthogonal basis, e1 ^ e2 is a pure bivector while e1 * e2 also
contains the Gram entry. Blade labels do not turn exterior products into
geometric products or perform a basis change.
For n basis vectors, a multivector stores 2**n coefficients. alg.n is
the vector-space dimension and alg.dim is the coefficient count. Dense
storage and some operations grow exponentially; this is not a sparse or
arbitrary-precision engine.
Choose presets or individual presentation components
Complete presets select the metric, blade convention, notation, local-name policy, display order and optional model metadata together:
from galaga import Algebra, presets
sta = Algebra(config=presets.sta("mostly-minus"))
pga = Algebra(config=presets.pga(spatial_dim=3))
cga = Algebra(config=presets.cga(spatial_dim=3, frame="null"))
rga = Algebra(config=presets.rga())
lengyel_cga = Algebra(config=presets.lengyel_cga())
# Inspect the immutable expanded configuration before constructing an algebra.
config = presets.euclidean(3).build()
euclidean = Algebra(config=config)
Native-null CGA now defaults to $(e_o,e_1,\ldots,e_n,e_\infty)$.
Use presets.cga(n, basis_order="euclidean-first") for the former native
coordinate order, including when loading old coefficient arrays.
This is a real basis choice, not a display override: higher-grade coordinates
must be converted as well as vector coordinates. The default has
$e_o\wedge I_E\wedge e_\infty=\mathrm{algebra.I}$; the compatibility order
retains the factor $(-1)^n$. Orthogonal and Lengyel CGA remain unchanged.
The CGA guide explains orientation and migration.
CGA presets display the native pseudoscalar as I. Opt into paired
IE/IC Python names (LaTeX $I_E$/$I_C$) with model_pseudoscalars=True
and name eo ^ einf as E
with pseudoscalar_null=True. An explicit pss="J" overrides the native top
label; pss=None uses automatic naming. These are naming choices only:
in Euclidean-first 3D, native I displays as $-I_C$ when model names are
selected. In 1D, the Euclidean vector remains e1, with IE lookup-only.
The complete factories are euclidean, sta, pga, cga, rga,
lengyel_cga, complex, quaternion and exterior. Complex and quaternion
presets describe even subalgebras of real Euclidean algebras, not complex
coefficient storage. The older p_* factories remain explicit compatibility
imports; new code should use presets.
Select only blade names or operation notation when the metric is already known:
from galaga import Algebra, presets
sta = Algebra(
1, 3,
blades=presets.blades.sta(sigmas=True, pseudovectors=True),
notation=presets.notation.functional(),
)
g0, g1, g2, g3 = sta.basis_vectors()
assert sta.blade("s1") == g1 * g0
# config= defines the metric; presentation components can still override it.
cga = Algebra(
config=presets.cga(3),
notation=presets.notation.doran_lasenby(),
)
config= cannot be combined with numeric constructor arguments such as
gram= or (p, q, r). It can be combined with presentation=, blades=,
notation=, local_names=, display_order= and display= overrides.
Blade recipes resolve against the target algebra. Metric-aware STA sigma and pseudovector signs are derived from the actual ordered unit-diagonal metric; unsupported metrics are rejected. CGA recipes validate the selected null or orthogonal frame. Blade-only recipes do not install model metadata or change the metric; arbitrary vocabulary choices do not guarantee model semantics.
presets.notation offers default(), functional(), functional_short(),
doran_lasenby(), hestenes(), lengyel() and lengyel_rga(). These change
rendering, not the operation called. See the preset lesson
and presentation guide for lower-level customization.
Values, blades, names and expressions
Multivector values display by grade, then lexicographically by numeric basis
indices (e12, e13, e14, e23, ...). Explicit preset conventions, including RGA
and quaternion order, are preserved. DisplayOrder controls presentation only;
coefficient storage and basis enumeration remain in native bitmask order.
from galaga import Algebra, Name, exp
from galaga.expression import evaluate
alg = Algebra(3)
e1, e2, e3 = alg.basis_vectors(expr=True)
B = (e1 ^ e2).named("B")
theta = alg.scalar(0.6).named(Name.from_latex(r"\theta"))
R = exp(-theta * B / 2).named("R")
print(R.display("full/latex"))
assert R.unnamed() == R.without_expr() == R
mass = alg.scalar(3).named("m")
square = mass**2
replayed = evaluate(square.expr, algebra=alg, environment={"m": 5})
assert float(replayed) == 25
assert float(square) == 9 # Rebinding does not change the original eager value.
named(), unnamed(), with_expr() and without_expr() return new
wrappers. Naming alone does not attach an expression to that value, but
operations on named or tracked operands record provenance. expr=True on
factories records construction provenance. Replay of symbols requires explicit
bindings; expression history does not make coefficients symbolic.
To opt in once, use Algebra(..., expr=True) (also with config=). The default
is False. Factories inherit it unless overridden, as do ConformalModel and
RigidModel; presentation-derived algebra views preserve it.
from galaga import Algebra
tracked_alg = Algebra(2, expr=True)
a, b = tracked_alg.basis_vectors()
assert (a * b).expr is not None
plain = tracked_alg.vector([1, 2], expr=False)
assert (plain + 1).expr is None
Name holds ASCII, Unicode and LaTeX spellings. Use Name.from_latex(...)
for supported LaTeX-to-name conversion, or supply each spelling explicitly;
ordinary strings are literal and are not automatically parsed as LaTeX.
blade() accepts configured labels, native bitmasks, signed blade references
or existing signed unit blades. blades() is the ordered plural form:
from galaga import Algebra, presets
alg = Algebra(config=presets.rga())
e1, e2, e3, e4 = alg.basis_vectors()
e23, e31 = alg.blades(e2 ^ e3, e3 ^ e1, expr=True)
assert e31 == alg.blade("e31") == -alg.blade(0b0101)
vector = alg.vector([1, 2, 3, 0])
assert vector == e1 + 2 * e2 + 3 * e3
alg.locals() returns a read-only mapping of configured local names. Prefer
explicit bindings in reactive notebooks so dependencies remain visible.
Presentation order never changes coefficient storage or native basis order.
Grades, scalar conversion and floating-point comparisons
from galaga import Algebra, grade, grades, scalar_part
alg = Algebra(3)
e1, e2, e3 = alg.basis_vectors()
value = 2 + e1 + 3 * (e1 ^ e2)
assert grade(value, 1) == e1
assert grades(value, [0, 2]) == 2 + 3 * (e1 ^ e2)
assert float(grade(value, 0)) == scalar_part(value) == 2
coefficients = value.data # Read-only NumPy array, indexed by blade bitmask.
assert coefficients[0b0011] == 3
assert not coefficients.flags.writeable
a = alg.scalar(0.1) + alg.scalar(0.2)
b = alg.scalar(0.3)
assert a != b
assert a.almost_equal(b, atol=1e-12)
nearly_scalar = 2 + 1e-13 * e1
assert nearly_scalar != 2 # Equality sees every nonzero coefficient.
assert float(nearly_scalar) == 2 # Scalar conversion has a 1e-12 tolerance.
assert nearly_scalar.data[1] != 0 # Conversion does not change the stored value.
float(value) checks scalarity with an absolute coefficient tolerance of
1e-12: larger nonscalar components raise TypeError, but components at or
below that threshold can be ignored. It is not an exact scalarity check.
For deliberate scalar extraction from any multivector, use grade(value, 0)
for a scalar multivector or scalar_part(value) for its Python float
coefficient. To require exact scalarity, check that every entry of
value.data[1:] is zero before conversion.
Equality and hashing use exact stored values, not rounded display text or a
tolerance. Multivector comparisons require the same underlying numeric algebra;
presentation views share that identity, independently constructed algebras do
not. Use .almost_equal(..., atol=...) for absolute-tolerance comparisons
within one algebra. Display zero tolerance does not erase coefficients.
Multivectors do not implement NumPy array or ufunc protocols; use .data
explicitly rather than np.asarray(value) to obtain coefficients.
Choose the intended inner product
There is deliberately no unqualified ip or inner_product. For homogeneous
grades r and s, the operations have different definitions and purposes:
| Operation | Definition | Typical intent |
|---|---|---|
scalar_product(A, B) |
Scalar part of A * B |
Scalar coefficient of the geometric product |
metric_inner_product(A, B) |
Scalar part of A * ~B |
Metric-induced pairing on exterior blades |
left_contraction(A, B) |
Grade s-r of A * B if r <= s, else zero |
Remove the left grade from the right |
right_contraction(A, B) |
Grade r-s of A * B if r >= s, else zero |
Remove the right grade from the left |
hestenes_inner(A, B) |
Grade abs(r-s) of A * B if both grades are nonzero |
Symmetric grade selection excluding scalars |
doran_lasenby_inner(A, B) |
Grade abs(r-s) of A * B, including scalar grades |
Symmetric grade selection including scalars |
Mixed-grade inputs are handled by summing over homogeneous pairs. “Symmetric grade selection” does not mean the operation commutes. All six agree on vector inputs, but a bivector's metric pairing differs from its scalar product:
from galaga import Algebra, metric_inner_product, scalar_product
alg = Algebra(gram=[[2.0, 1.0], [1.0, 3.0]])
e1, e2 = alg.basis_vectors()
B = e1 ^ e2
G = alg.gram
determinant = G[0, 0] * G[1, 1] - G[0, 1] * G[1, 0]
assert float(metric_inner_product(B, B)) == determinant
assert float(scalar_product(B, B)) == -determinant
norm2(A) is metric_inner_product(A, A) and norm(A) is
sqrt(abs(norm2(A))). These are metric quantities, not Euclidean norms of
the coefficient array: a nonzero null value can have zero norm. With named or
tracked inputs, norm returns a scalar multivector to retain provenance;
float(norm(A)) obtains the magnitude in either case.
The inner-products lesson explains the intent, scalar cases,
contraction signs and differing author conventions. Related APIs include
regressive_product, antidot_product, transwedge and
transwedge_antiproduct; see the RGA guide for their model context.
geometric_product(a, b, c) and outer_product(a, b, c) fold left-to-right.
commutator and lie_bracket return ab - ba; anticommutator and
jordan_product return ab + ba. Use half_commutator or
half_anticommutator when the definition includes a factor of one half.
Rotors, logarithms and generators
import math
from galaga import (
Algebra, exp, is_rotor, is_rotor_generator, log, rotor_generator, sandwich,
)
alg = Algebra(3)
e1, e2, e3 = alg.basis_vectors()
generator = -(math.pi / 4) * (e1 ^ e2) # Half-angle for a quarter-turn.
R = exp(generator)
assert is_rotor_generator(generator)
assert is_rotor(R)
assert sandwich(R, e1).almost_equal(e2) # R * e1 * ~R
assert rotor_generator(R).almost_equal(generator)
# The mathematical logarithm is not restricted to rotors.
A = exp(0.3 * e1)
assert log(A).almost_equal(0.3 * e1)
is_rotor checks evenness, the full unit reverse product and preservation of
vectors under the reverse sandwich. Evenness alone is insufficient.
sandwich(R, x) uses reversion, not a substituted inverse or automatic
normalization. R.dag also means reverse; .bar, .inv and .sq mean
grade involution, inverse and geometric square respectively.
log(A) computes the real principal algebra logarithm where supported.
It accepts more than rotors, but not every invertible value: singular inputs,
the nonpositive-real spectral branch cut and unresolved numerical cases raise.
It does not complexify the algebra or search alternative logarithm branches.
rotor_generator(R) checks the rotor's principal logarithm as a geometric
generator; is_rotor_generator(B) checks a candidate independently. A valid
algebra logarithm need not generate a path of rotors, even if its exponential
is a rotor. The returned generator retains its full scale and any half-angle.
Explore the logarithms and generators lesson.
Rendering and notebook tables
Default auto content shows a deduplicated expression/name = value equality
when provenance or a name is present; unnamed, untracked values show just their
value. Enable tracking once for a notebook:
from galaga import Algebra, presets
alg = Algebra(config=presets.euclidean(3), expr=True)
e1, e2, e3 = alg.basis_vectors()
result = (e1 + e2) ^ e3
result # Rich display: (e₁ + e₂) ∧ e₃ = e₁₃ + e₂₃
Use result.latex(content="value") for just the result, or set
display=DisplayPolicy(content="value") on the algebra to keep provenance
without displaying it by default.
Rendering content (auto, name, expr, value, full) is independent of the
target (ascii, unicode, latex). Persistent presentation changes return
cheap algebra views; scoped changes are isolated by thread and async task:
from galaga import Algebra, DisplayPolicy, presets
alg = Algebra(3)
e1, e2, e3 = alg.basis_vectors(expr=True)
x = (e1 ^ e2).named("x")
teaching = alg.with_notation(presets.notation.functional()).with_display(
DisplayPolicy(content="full")
)
assert teaching.numeric is alg.numeric
with alg.use_presentation(teaching.presentation):
print(x.latex())
print(x.display("value/ascii"))
Rich notebook display works directly on multivectors and algebra tables.
Raw .latex() output has no math delimiters; the rich-display hook adds them.
To scope just operation notation, use alg.use_notation(...). In Marimo,
explicitly render inside the scope so the selected notation is still active:
import marimo as mo
from galaga import Algebra, metric_inner_product as mip, presets
alg = Algebra(config=presets.euclidean(3), expr=True)
e1, _, _ = alg.basis_vectors()
with alg.use_notation(presets.notation.lengyel()):
mo.output.replace(mo.as_html(mip(e1, e1)))
This preserves other presentation settings. A multivector displayed after the scope exits uses the restored notation, not the notation at calculation time.
For a reusable notebook display choice, use an immutable presenter. It captures the selected rendering on a view while leaving the mathematical value unchanged:
from galaga import Algebra, Presenter, metric_inner_product as mip, presets
alg = Algebra(config=presets.euclidean(3), expr=True)
e1, e2, e3 = alg.basis_vectors()
lengyel = presets.presenters.lengyel()
teaching = Presenter(content="full")
pairing_view = lengyel(mip(e1, e1))
result_view = teaching((e1 + e2) ^ e3)
pairing_view # e₁ • e₁ = 1
result_view # (e₁ + e₂) ∧ e₃ = e₁₃ + e₂₃
next_value = pairing_view.value + e1 # views deliberately are not arithmetic
See presentation configuration and the reusable presenters lesson.
from galaga import Algebra, presets
cga = Algebra(config=presets.cga(3))
gram_table = cga.bilinear_form_table()
print(gram_table) # Aligned text with the default Unicode presentation.
vector_table = cga.wedge_product_table(colour=True)
full_table = Algebra(3).wedge_product_table(full=True, color=True)
gram_table # Last line of a notebook cell: rich display. Try vector_table/full_table too.
bilinear_form_table() labels the native Gram matrix; numeric entries remain
available as alg.gram. Its full=True option shows the induced pairing
metric_inner_product(A, B) = <A * ~B>_0 on every blade, not geometric-product
scalar extraction. An orthonormal Euclidean basis gives an identity table.
wedge_product_table() shows row blade wedged with
column blade, using basis vectors by default. For either table, full=True includes scalar
1 and all exterior blades in the algebra's display order, including preset
and user overrides (grade-then-lexicographic by default). A full table has
4**n result cells: an 8-by-8 table for Algebra(3), but 32-by-32 for 3D CGA.
Both are immutable presentation snapshots with signed native blade headings.
Exact zeros render in grey (#bbbbbb) in LaTeX; small nonzero entries are not
hidden by display tolerance. Either color=True or colour=True enables
wedge-result grade colouring; all flags default to False and require booleans.
Text output has no colour escapes. Unlike Gram entries, exterior products in
the fixed native basis are independent of the metric.
On Python 3.14+, install the helper with
python -m pip install --pre "galaga-marimo>=2.0.0a4,<3", then interpolate
values and tables into dynamic Markdown:
import galaga_marimo as gm
from galaga import Algebra, presets
alg = Algebra(config=presets.cga(3))
table = alg.bilinear_form_table()
document = gm.md(t"""The native bilinear form is:
{table}
""")
document
The table supplies its display-math block; do not add another $$ wrapper
around {table}. The bilinear and wedge lesson teaches the
underlying geometry, including null vectors versus degenerate metrics.
Geometric models
Native-null conformal geometric algebra (CGA)
The null-frame preset stores origin and infinity as actual null basis vectors,
with their mutual pairing in the Gram matrix. Attach ConformalModel for
lifting coordinates and working with geometric objects:
from galaga import Algebra, outer_product, presets
from galaga.cga import ConformalModel
alg = Algebra(config=presets.cga(spatial_dim=3, frame="null"))
cga = ConformalModel(alg, expr=True)
a = cga.up((0, 0, 0))
b = cga.up((1, 0, 0))
line = outer_product(a, b, cga.infinity)
assert line.homogeneous_grade() == 3
round_point = cga.round_point((3, 4, 0), radius_squared=4)
assert float(cga.center_norm(round_point)) == 5
assert float(cga.radius_norm(round_point)) == 2
expanded = cga.with_expression_form("expanded")
assert cga.carrier(round_point) == expanded.carrier(round_point)
The CGA guide covers rounds, flats, duality, projection,
transformations, weighted norms and operator versus expanded expression forms.
The CGA Gram lesson constructs the model from its bilinear form.
Here spatial_dim=3 means five algebra basis vectors, not three.
Point-based rigid geometric algebra (RGA)
presets.rga() selects Eric Lengyel's point-based interpretation of
Cl(3, 0, 1). It shares a signature with plane-based PGA but has different
geometric meanings and conventions:
from galaga import Algebra, presets
from galaga.rga import RigidModel
alg = Algebra(config=presets.rga())
rga = RigidModel(alg, expr=True)
p = rga.point((3, 4, 0)).named("P")
q = rga.point((1, 0, 0)).named("Q")
line = p ^ q
assert float(rga.bulk_norm(p)) == 5
assert rga.is_valid_line(line)
The RGA guide covers paired norms, projective measurements, projection, support, line/motor/flector constraints and the dual relationship with plane-based PGA. Merely changing blade labels does not attach a model.
Optional packages and learning resources
These are separate installations, not dependencies of the numeric package:
| Package | Python | Purpose |
|---|---|---|
| galaga-matrix | 3.11+ | Rich matrix representations, conversions and supported spinor/quaternion modes |
| galaga-marimo | 3.14+ | T-string Markdown interpolation and notebook helpers |
| galaga-anywidget | 3.11+ | Interactive geometric visualizations |
| galaga-mermaid | 3.11+ | Expression-tree diagrams |
For example, install galaga-matrix with
python -m pip install --pre "galaga-matrix>=2.0.0a4,<3". Its left-regular
representation supports any symmetric Gram matrix; compact conversion is
explicit for numerically suitable general nondegenerate Gram metrics and
round-trips only when the selected representation is injective. Complex matrices
do not change the real coefficient domain of Galaga multivectors. See the
matrix lessons.
From a repository checkout, make run-marimo opens the example gallery using
Python 3.14 and editable local companion packages. Start with
construction, presets,
eager values and expressions, tables,
and inner products. See the documentation index for
the full collection. README Python snippets are executed by the test suite;
the maintained notebooks also have headless execution coverage.
Numeric-only use and migration
Application code normally imports from galaga. Use galaga.core when names,
expression provenance and rendering are not needed:
from galaga.core import Algebra, geometric_product
alg = Algebra(gram=[[1.0, 0.2], [0.2, 1.0]])
e1, e2 = alg.basis_vectors()
result = geometric_product(e1, e2)
assert float(result.grade(0)) == alg.gram[0, 1]
The public facade composes these numeric values with presentation; it is not a second engine. See the numeric core guide for its lower-level API.
The Galaga 1 engine and temporary galaga.legacy namespace no longer ship.
The final-API cleanup after 2.0.0a4 also removes galaga.gram_bridge and
the temporary involute, mag2, magnitude_squared, norm_squared,
normalise and normalize functions. Use grade_involution, norm2 and
unit respectively. Earlier alphas still provide those migration adapters;
permanent concise aliases and explicit p_* preset imports remain supported.
Old expr, symbolic_core, notation and latex_* implementation paths
are replaced by galaga.expression, galaga.presentation, galaga.rendering
and galaga.names. Follow the migration guide for explicit
construction, naming, provenance and operation replacements.
Metadata
Release files for galaga 2.0.0a6
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | Provenance | Actions |
|---|---|---|---|---|
| galaga-2.0.0a6.tar.gz | 629.4 kB | Without provenance | Details for galaga-2.0.0a6.tar.gz |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Actions Reset |
|---|---|---|---|---|
| galaga-2.0.0a6-py3-none-any.whl | Python 3 | none | any | Details for galaga-2.0.0a6-py3-none-any.whl |
Total release size: 751.8 kB
Release files / galaga-2.0.0a6.tar.gz
| Download URL | galaga-2.0.0a6.tar.gz |
|---|---|
| Size | 629.4 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
bd92bf6aa7ae396e329cef5b5f5fa4e6e6d129ab5f10b0aaf02fe307f9e14556
|
|
BLAKE2b-256 checksum How to use checksums |
2983734938ebda9188ea32d8df5a206f3348223bbbbd761e56cf0381e701d58f
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
uv/0.11.7 {"installer":{"name":"uv","version":"0.11.7","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"macOS","version":null,"id":null,"libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}
|
Release files / galaga-2.0.0a6-py3-none-any.whl
| Download URL | galaga-2.0.0a6-py3-none-any.whl |
|---|---|
| Size | 122.4 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
2a37d58ad542eaa4ad99a15b54caff4eb1d0f117e183c2f216e967d5b5727487
|
|
BLAKE2b-256 checksum How to use checksums |
23473dcb0930ecef28f67aa79da2c13a79c63bf754990561b6599bb5ad129449
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
uv/0.11.7 {"installer":{"name":"uv","version":"0.11.7","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"macOS","version":null,"id":null,"libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}
|
Security for galaga 2.0.0a6
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
No provenance found
None of the files in this release were uploaded with provenance metadata.
A note on package security
PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.
To learn more about protecting yourself from malicious actors, visit our package security documentation.