Skip to main content

Python bindings for the swisseph-rs crate — a pure-Rust Swiss Ephemeris

Project description

pyswisseph-rs

Python bindings for the swisseph-rs crate — a pure-Rust reimplementation of the Swiss Ephemeris.

Why this package? Every method on Ephemeris releases the GIL, so a single shared instance can drive a ThreadPoolExecutor at full core utilization with zero coordination. No global state, no swe_close(), no mutex — just pass the same object to every thread.

pip install pyswisseph-rs     # or: uv add pyswisseph-rs

Quickstart (zero files, Moshier ephemeris)

The built-in Moshier analytical ephemeris covers all planets for any date without ephemeris files. Accuracy is ~1 arc-second for modern dates.

from swisseph_rs import Body, CalcFlags, Ephemeris, EphemerisConfig

eph = Ephemeris(EphemerisConfig())   # Moshier by default, no files needed
result = eph.calc_ut(2451545.0, Body.SUN, CalcFlags.SPEED)

lon, lat, dist, lon_speed, lat_speed, dist_speed = result.data
print(f"Sun longitude at J2000: {lon:.6f}°")

Using Swiss Ephemeris data files

For sub-arc-second precision, point EphemerisConfig at a directory containing the Swiss Ephemeris data files:

from swisseph_rs import Ephemeris, EphemerisConfig, EphemerisSource

eph = Ephemeris(EphemerisConfig(
    ephemeris_source=EphemerisSource.SWISS,
    ephe_path="/path/to/ephe",
))

Data files (sepl*.se1, semo*.se1, seas*.se1, etc.) are available from Astrodienst. Download the files covering your date range and place them in the directory you point ephe_path to.

For JPL ephemerides, set ephemeris_source=EphemerisSource.JPL and optionally jpl_filename="de441.eph".

Threading

pyswisseph-rs is designed for concurrent workloads. Every Ephemeris method releases the GIL around the Rust computation, so multiple Python threads run in true parallel on separate cores.

import concurrent.futures
from swisseph_rs import Body, CalcFlags, Ephemeris, EphemerisConfig

eph = Ephemeris(EphemerisConfig())
bodies = [Body.SUN, Body.MOON, Body.MERCURY, Body.VENUS, Body.MARS,
          Body.JUPITER, Body.SATURN]

def calc_year(year_offset):
    """Calculate daily positions for one year."""
    jd_start = 2451545.0 + year_offset * 365.25
    results = []
    for day in range(365):
        for body in bodies:
            r = eph.calc_ut(jd_start + day, body, CalcFlags.SPEED)
            results.append(r.data[0])  # longitude
    return results

# Same Ephemeris instance shared across all threads — no copies, no locks
with concurrent.futures.ThreadPoolExecutor(max_workers=4) as pool:
    futures = [pool.submit(calc_year, y) for y in range(20)]
    all_results = [f.result() for f in futures]

This consistently achieves near-linear speedup (e.g. ~3.5x on 4 cores). Results are bit-identical to serial execution.

What about multiprocessing?

Ephemeris is not picklable. For process-based parallelism, construct a separate Ephemeris in each worker. But threading is usually the better choice here — there is no GIL contention, so threads give you the speedup without the IPC overhead.

Sidereal mode and topographic position

Configuration that the C library sets via global state (swe_set_sid_mode, swe_set_topo) is passed through EphemerisConfig instead:

from swisseph_rs import (
    Ephemeris, EphemerisConfig, SiderealMode, TopoPosition,
)

eph = Ephemeris(EphemerisConfig(
    sidereal_mode=SiderealMode.LAHIRI,
    topographic=TopoPosition(longitude=-74.006, latitude=40.7128, altitude=10.0),
))

The config is frozen after construction — no mutable global state, no ordering bugs between set_* calls.

Migrating from C pyswisseph

pyswisseph-rs wraps the same Swiss Ephemeris engine but replaces the C library's global-state API with an object-oriented, stateless design. The key differences:

  1. No global state. swe_set_ephe_path, swe_set_sid_mode, swe_set_topo become fields on EphemerisConfig. swe_close is unnecessary.
  2. Methods on Ephemeris. Functions like swe_calc become eph.calc().
  3. Named types. Flags are CalcFlags.SPEED, bodies are Body.SUN, not raw integers.

Every method's docstring includes the corresponding swe_* name. Use help(eph.calc) or check the type stubs for the mapping.

Function mapping (top 25)

C pyswisseph pyswisseph-rs
swe_calc(jd, ipl, iflag) eph.calc(jd, body, flags)
swe_calc_ut(jd, ipl, iflag) eph.calc_ut(jd, body, flags)
swe_calc_pctr(jd, ipl, ictr, iflag) eph.calc_pctr(jd, body, center, flags)
swe_fixstar2(star, jd, iflag) eph.fixstar2(star, jd, flags)
swe_fixstar2_ut(star, jd, iflag) eph.fixstar2_ut(star, jd, flags)
swe_fixstar2_mag(star) eph.fixstar2_mag(star)
swe_houses(jd, lat, lon, hsys) eph.houses(jd, lat, lon, hsys)
swe_houses_ex(jd, iflag, lat, lon, hsys) eph.houses_ex(jd, flags, lat, lon, hsys)
swe_house_pos(armc, lat, eps, hsys, ...) houses.house_pos(armc, lat, eps, hsys, xpin)
swe_get_ayanamsa_ex(jd, iflag) eph.get_ayanamsa_ex(jd, flags)
swe_julday(y, m, d, h, cal) date.julday(y, m, d, h, cal)
swe_revjul(jd, cal) date.revjul(jd, cal)
swe_utc_to_jd(y,m,d,h,mi,s, cal) date.utc_to_jd(utc, cal, eph)
swe_day_of_week(jd) date.day_of_week(jd)
swe_rise_trans(...) eph.rise_trans(...)
swe_pheno_ut(jd, ipl, iflag) eph.pheno_ut(jd, body, flags)
swe_nod_aps_ut(jd, ipl, iflag, method) eph.nod_aps_ut(jd, body, flags, method)
swe_sol_eclipse_when_glob(jd, iflag, ifltype, bwd) eph.sol_eclipse_when_glob(jd, flags, ifltype, backward)
swe_lun_eclipse_when(jd, iflag, ifltype, bwd) eph.lun_eclipse_when(jd, flags, ifltype, backward)
swe_get_orbital_elements(jd, ipl, iflag) eph.get_orbital_elements(jd, body, flags)
swe_split_deg(ddeg, roundflag) math.split_degrees(ddeg, flags)
swe_sidtime(jd) sidereal_time.sidereal_time(jd, config)
swe_refrac(inalt, atpress, attemp, dir) azalt.refrac(inalt, atpress, attemp, dir)
swe_set_ephe_path(path) EphemerisConfig(ephe_path=path)
swe_set_sid_mode(sid_mode, t0, ayan_t0) EphemerisConfig(sidereal_mode=..., sidereal_t0=..., sidereal_ayan_t0=...)
swe_set_topo(lon, lat, alt) EphemerisConfig(topographic=TopoPosition(...))
swe_close() (not needed — no global state)

Module structure

Free functions live in submodules mirroring the Rust crate's module tree:

from swisseph_rs import date, math, houses, azalt, sidereal_time

Types and flags are re-exported at the top level:

from swisseph_rs import Body, CalcFlags, EphemerisConfig, Ephemeris

Design and transliteration discipline

pyswisseph-rs maintains a strict 1:1 correspondence with the underlying Rust crate's public API. Every Python class, enum, and function mirrors exactly one Rust symbol. This is a deliberate design choice documented in CONTEXT.md and the architecture decision records in docs/adr/:

  • ADR 0001 — PyO3 on the lib crate, not the C FFI layer
  • ADR 0002 — strict 1:1 transliteration from Rust to Python

License

This project is licensed under the GNU Affero General Public License v3.0 or later (AGPL-3.0-or-later). See LICENSE for the full text.

The wheel statically links the swisseph-rs crate, which is a derivative work of the Swiss Ephemeris by Astrodienst AG, also licensed under AGPL-3.0-or-later.

Project details


Download files

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

Source Distribution

pyswisseph_rs-0.1.0.tar.gz (147.6 kB view details)

Uploaded Source

Built Distributions

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

pyswisseph_rs-0.1.0-cp311-abi3-win_amd64.whl (916.6 kB view details)

Uploaded CPython 3.11+Windows x86-64

pyswisseph_rs-0.1.0-cp311-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl (1.1 MB view details)

Uploaded CPython 3.11+manylinux: glibc 2.17+ x86-64

pyswisseph_rs-0.1.0-cp311-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl (1.1 MB view details)

Uploaded CPython 3.11+manylinux: glibc 2.17+ ARM64

pyswisseph_rs-0.1.0-cp311-abi3-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl (2.1 MB view details)

Uploaded CPython 3.11+macOS 10.12+ universal2 (ARM64, x86-64)macOS 10.12+ x86-64macOS 11.0+ ARM64

File details

Details for the file pyswisseph_rs-0.1.0.tar.gz.

File metadata

  • Download URL: pyswisseph_rs-0.1.0.tar.gz
  • Upload date:
  • Size: 147.6 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for pyswisseph_rs-0.1.0.tar.gz
Algorithm Hash digest
SHA256 57c065aa4cc9da031bbee46f8819d6fb8185408d395d568ad9fbcfb84b807693
MD5 a361df6a1333de3cf9aa6eb216c0c55d
BLAKE2b-256 f25d2853bf794c0c83937407b6061acaebbdb4bace11490c13c00ab77a31905f

See more details on using hashes here.

File details

Details for the file pyswisseph_rs-0.1.0-cp311-abi3-win_amd64.whl.

File metadata

File hashes

Hashes for pyswisseph_rs-0.1.0-cp311-abi3-win_amd64.whl
Algorithm Hash digest
SHA256 ee2a1c12fbae1f75675a367457a939f58e6cdf9fe8f6b4f365e4b7249770c2bc
MD5 edaadc8adba441ab624a6626a8e646bc
BLAKE2b-256 40bf2e2acc0dbc9fb9c096e007190df21aff606f4df89521e88dc0c4d67ed680

See more details on using hashes here.

File details

Details for the file pyswisseph_rs-0.1.0-cp311-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl.

File metadata

File hashes

Hashes for pyswisseph_rs-0.1.0-cp311-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
Algorithm Hash digest
SHA256 e33179f7dc1d9cffeada35a903ae818b6f62f61b2e6d6e4d5759c1285a2369a7
MD5 91075adfe92a695eb1f9753c52b0a063
BLAKE2b-256 16ccfc1640ae2b239a89b41eccac6b99481e19af95cd93e8da416920c876eefc

See more details on using hashes here.

File details

Details for the file pyswisseph_rs-0.1.0-cp311-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl.

File metadata

File hashes

Hashes for pyswisseph_rs-0.1.0-cp311-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl
Algorithm Hash digest
SHA256 e751672cdbd1e5d47a66ded0d5eecd5f3f6c09df95b4f4218ba1421a8665544f
MD5 b3ef348fb0b98fb54126a807573fbb32
BLAKE2b-256 03d6bd95f1d0c5ea46eca661fe1d265b9ce313250cbd6a9ffec8afdc76a7198a

See more details on using hashes here.

File details

Details for the file pyswisseph_rs-0.1.0-cp311-abi3-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl.

File metadata

File hashes

Hashes for pyswisseph_rs-0.1.0-cp311-abi3-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl
Algorithm Hash digest
SHA256 2ef76bc899484998e0765eebd0e52bfc88d50fde5fe80c4ee12a760198b728db
MD5 f4d80cc3ea50d6b27ac11dda7387780e
BLAKE2b-256 8e777cec7d535b16f78dd7f5d31f26845a7c563c372d3ac92cf2b82016ceb077

See more details on using hashes here.

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