|
146 | 146 | } |
147 | 147 | ) |
148 | 148 |
|
| 149 | +# Every name accepted in ``active_points``. Unknown names (typos) must raise |
| 150 | +# instead of silently vanishing from the calculation loop. |
| 151 | +_VALID_ACTIVE_POINT_NAMES = frozenset(get_args(AstrologicalPoint)) |
| 152 | + |
149 | 153 | # Declarative mapping of geometrically opposite point pairs. |
150 | 154 | # Each derived point is computed as primary.abs_pos + 180 (mod 360). |
151 | 155 | # negate_speed/negate_dec/negate_lat control whether speed, declination and |
|
191 | 195 | bid for bid in ( |
192 | 196 | getattr(ephe, "MEAN_NODE", None), getattr(ephe, "TRUE_NODE", None), |
193 | 197 | getattr(ephe, "MEAN_APOG", None), getattr(ephe, "OSCU_APOG", None), |
194 | | - getattr(ephe, "INTP_APOG", None), |
| 198 | + getattr(ephe, "INTP_APOG", None), getattr(ephe, "INTP_PERG", None), |
195 | 199 | ) if bid is not None |
196 | 200 | ) |
197 | 201 |
|
| 202 | +# Point names dropped by the exclusion above (plus their derived opposites, |
| 203 | +# which cannot exist without their primaries). Consumers that diagnose |
| 204 | +# missing points (e.g. the transit factory's misconfiguration warning) use |
| 205 | +# this to recognize by-design absences in non-geocentric frames. |
| 206 | +_GEOCENTRIC_ONLY_POINT_NAMES = frozenset( |
| 207 | + { |
| 208 | + "Mean_North_Lunar_Node", "True_North_Lunar_Node", |
| 209 | + "Mean_South_Lunar_Node", "True_South_Lunar_Node", |
| 210 | + "Mean_Lilith", "True_Lilith", "Interpolated_Lilith", |
| 211 | + "Mean_Priapus", "True_Priapus", "Interpolated_Perigee", |
| 212 | + } |
| 213 | +) |
| 214 | + |
198 | 215 |
|
199 | 216 | def _degenerate_center_body_id(perspective_type: Optional[str]) -> Optional[int]: |
200 | 217 | """Body id that IS the origin of ``perspective_type``. |
@@ -824,6 +841,9 @@ def from_birth_data( |
824 | 841 | - If invalid zodiac/sidereal mode combinations are specified |
825 | 842 | - If GeoNames data is missing or invalid |
826 | 843 | - If timezone localization fails (ambiguous DST times) |
| 844 | + - If active_points contains unknown point names, or is an |
| 845 | + empty list (pass None to use the defaults). Fixed star |
| 846 | + names are redirected to active_fixed_stars with a warning. |
827 | 847 |
|
828 | 848 | Examples: |
829 | 849 | >>> # Basic natal chart with online location lookup |
@@ -885,6 +905,16 @@ def from_birth_data( |
885 | 905 | active_points_list: List[AstrologicalPoint] = list(DEFAULT_ACTIVE_POINTS) |
886 | 906 | else: |
887 | 907 | active_points_list = list(active_points) |
| 908 | + if not active_points_list: |
| 909 | + # An empty list means 'no filter' downstream (_should_calculate |
| 910 | + # treats it as falsy), which would silently invert the caller's |
| 911 | + # explicit "nothing" into a FULL chart — the exact inversion the |
| 912 | + # emptied-list branches below fail loudly for. Reject it up |
| 913 | + # front; None is the documented way to request the defaults. |
| 914 | + raise KerykeionException( |
| 915 | + "active_points is an empty list. Pass None (or omit it) to use " |
| 916 | + "DEFAULT_ACTIVE_POINTS, or list at least one point to calculate." |
| 917 | + ) |
888 | 918 | # v6: ``active_points`` is no longer a channel for fixed stars. |
889 | 919 | # Star names that v5 accepted here (e.g. "Regulus", "Spica") are |
890 | 920 | # redirected to the ``active_fixed_stars`` channel — with a |
@@ -928,6 +958,19 @@ def from_birth_data( |
928 | 958 | _merged_stars.append(_star) |
929 | 959 | active_fixed_stars = _merged_stars |
930 | 960 |
|
| 961 | + # Anything left must be a real AstrologicalPoint: an unknown name |
| 962 | + # (e.g. a typo like "Sunn") would otherwise never be iterated by |
| 963 | + # the calculation loop and simply vanish from the chart — a silent |
| 964 | + # wrong result rather than an error. |
| 965 | + _unknown_points = [p for p in active_points_list if p not in _VALID_ACTIVE_POINT_NAMES] |
| 966 | + if _unknown_points: |
| 967 | + raise KerykeionException( |
| 968 | + f"Unknown active_points {_unknown_points}: not valid astrological " |
| 969 | + "points (and not fixed star names, which are redirected to " |
| 970 | + "active_fixed_stars). Check the AstrologicalPoint literal in " |
| 971 | + "kerykeion.schemas.kr_literals for valid names." |
| 972 | + ) |
| 973 | + |
931 | 974 | # The center body of the perspective has no position as seen from itself |
932 | 975 | # (Earth in geocentric/topocentric, Sun in heliocentric, the center |
933 | 976 | # planet in a planetocentric chart), so drop it from the active points |
@@ -957,6 +1000,30 @@ def from_birth_data( |
957 | 1000 | "one other point or omit active_points." |
958 | 1001 | ) |
959 | 1002 |
|
| 1003 | + # Geocentric-only points (lunar nodes, Lilith/apogee variants) have no |
| 1004 | + # meaning in non-geocentric frames; the calculation loop drops them |
| 1005 | + # (see _GEOCENTRIC_ONLY_BODY_IDS). Mirror the center-body pass: give |
| 1006 | + # the user-facing warning here instead of a silent disappearance, and |
| 1007 | + # reject a list that would empty out (the 'no filter' inversion). |
| 1008 | + if perspective_type not in _GEO_TOPO_PERSPECTIVES: |
| 1009 | + _geo_only_dropped = [p for p in active_points_list if p in _GEOCENTRIC_ONLY_POINT_NAMES] |
| 1010 | + if _geo_only_dropped: |
| 1011 | + logging.warning( |
| 1012 | + "Excluding %s from active_points: geocentric-only points " |
| 1013 | + "(lunar nodes, Lilith/apogee variants) have no meaning in " |
| 1014 | + "the %r perspective.", |
| 1015 | + _geo_only_dropped, |
| 1016 | + perspective_type, |
| 1017 | + ) |
| 1018 | + active_points_list = [p for p in active_points_list if p not in _GEOCENTRIC_ONLY_POINT_NAMES] |
| 1019 | + if not active_points_list: |
| 1020 | + raise KerykeionException( |
| 1021 | + f"active_points contained only {_geo_only_dropped}, which are " |
| 1022 | + f"geocentric-only points with no meaning in the " |
| 1023 | + f"{perspective_type!r} perspective. Include at least one " |
| 1024 | + "other point or omit active_points." |
| 1025 | + ) |
| 1026 | + |
960 | 1027 | calc_data["active_points"] = active_points_list |
961 | 1028 |
|
962 | 1029 | # Initialize configuration |
|
0 commit comments