g-battaglia / kerykeion

Data driven Astrology 💫
Kerykeion is a python library for astrology. It can generate SVG charts and extract all data about a birthchart, a synastry chart and a transit chart.
https://kerykeion.net
GNU Affero General Public License v3.0
297 stars 103 forks source link

Upgrade to pydantic 2 and fixing float-int presidence bug #98

Closed jackklika closed 8 months ago

jackklika commented 8 months ago

Overview

All tests are passing after these changes.

Rationale

Pydantic v1 will not be supported in June 30, 2025 according to the pydantic version policy. It's a long ways away, but will need to happen eventually. This was a quick fix so I implemented it.

Additionally if a project that uses this package is using pydantic 2, it will be unable to use the latest 4.2 version of this package that adds pydantic 1 support. This is what led me to make the change :-)

The only difficulties I anticipate are if projects that use this or dependent packages require pydantic 1 vs pydantic 2. But these will likely switch to pydantic 2 in the future. After all pydantic 2 is not released until summer 2024 so this could be premature. But merge if you'd like.

Float/int presidence bug

Initially I ran the pytest suite with the pydantic 2 upgrade and saw the following failures. These were only happening on pydantic 2 and worked on pydantic 1.

========================================================================== FAILURES ==========================================================================
__________________________________________________________ TestAstrologicalSubject.test_lunar_phase __________________________________________________________

self = <tests.test_kr_instance.TestAstrologicalSubject object at 0x102cb98d0>

    def test_lunar_phase(self):
>       assert self.subject.lunar_phase == self.expected_output["lunar_phase"]
E       AssertionError: assert {'degrees_bet...n_emoji': '🌖'} == {'degrees_bet...un_phase': 15}
E         Full diff:
E           {
E         +  'degrees_between_s_m': 201.07547248727388, 'moon_phase': 16, 'sun_phase': 15, 'moon_emoji': '🌖',
E         -  'degrees_between_s_m': 201,
E         -  'moon_emoji': '🌖',
E         -  'moon_phase': 16,
E         -  'sun_phase': 15,
E           }

tests/test_kr_instance.py:621: AssertionError
________________________________________________________________ TestCharts.test_birth_chart _________________________________________________________________

self = <tests.charts.test_charts.TestCharts object at 0x102cbba90>

    def test_birth_chart(self):
        birth_chart_svg = KerykeionChartSVG(self.first_subject).makeTemplate()
        birth_chart_svg_lines = birth_chart_svg.split("\n")

        with open(CURRENT_DIR / "expected_birth_chart.svg", "r") as f:
            file_content = f.read()

        file_content_lines = file_content.split("\n")

        for i in range(len(birth_chart_svg_lines)):
>           assert birth_chart_svg_lines[i] == file_content_lines[i]
E           assert "            ...349672458216'" == "            ...333333333334'"
E             -                             cx='-54.43333333333334'
E             +                             cx='-53.685349672458216'

tests/charts/test_charts.py:22: AssertionError
--------------------------------------------------------------------- Captured log call ----------------------------------------------------------------------
2023-12-27 20:07:16 INFO John Lennon birth location: Liverpool, 53.41058, -2.97794
_______________________________________________________________ TestCharts.test_synastry_chart _______________________________________________________________

self = <tests.charts.test_charts.TestCharts object at 0x102cbbbe0>

    def test_synastry_chart(self):
        synastry_chart_svg = KerykeionChartSVG(self.first_subject, "Synastry", self.second_subject).makeTemplate()
        synastry_chart_svg_lines = synastry_chart_svg.split("\n")

        with open(CURRENT_DIR / "expected_synastry_chart.svg", "r") as f:
            file_content = f.read()

        file_content_lines = file_content.split("\n")

        for i in range(len(synastry_chart_svg_lines)):
>           assert synastry_chart_svg_lines[i] == file_content_lines[i]
E           assert "            ...349672458216'" == "            ...333333333334'"
E             -                             cx='-54.43333333333334'
E             +                             cx='-53.685349672458216'

tests/charts/test_charts.py:34: AssertionError
--------------------------------------------------------------------- Captured log call ----------------------------------------------------------------------
2023-12-27 20:07:16 INFO John Lennon birth location: Liverpool, 53.41058, -2.97794
_______________________________________________________________ TestCharts.test_transit_chart ________________________________________________________________

self = <tests.charts.test_charts.TestCharts object at 0x102cbbd30>

    def test_transit_chart(self):
        transit_chart_svg = KerykeionChartSVG(self.first_subject, "Transit", self.second_subject).makeTemplate()
        transit_chart_svg_lines = transit_chart_svg.split("\n")

        with open(CURRENT_DIR / "expected_transit_chart.svg", "r", encoding="utf-8") as f:
            file_content = f.read()

        file_content_lines = file_content.split("\n")

        for i in range(len(transit_chart_svg_lines)):
>           assert transit_chart_svg_lines[i] == file_content_lines[i]
E           assert "            ...349672458216'" == "            ...333333333334'"
E             -                             cx='-54.43333333333334'
E             +                             cx='-53.685349672458216'

tests/charts/test_charts.py:46: AssertionError
--------------------------------------------------------------------- Captured log call ----------------------------------------------------------------------
2023-12-27 20:07:16 INFO John Lennon birth location: Liverpool, 53.41058, -2.97794
____________________________________________________________ TestCharts.test_external_natal_chart ____________________________________________________________

self = <tests.charts.test_charts.TestCharts object at 0x102cbbe80>

    def test_external_natal_chart(self):
        external_natal_chart_svg = KerykeionChartSVG(self.first_subject, "ExternalNatal").makeTemplate()
        KerykeionChartSVG(self.first_subject, "ExternalNatal").makeSVG()
        external_natal_chart_svg_lines = external_natal_chart_svg.split("\n")

        with open(CURRENT_DIR / "expected_external_natal_chart.svg", "r", encoding="utf-8") as f:
            file_content = f.read()

        file_content_lines = file_content.split("\n")

        for i in range(len(external_natal_chart_svg_lines)):
>           assert external_natal_chart_svg_lines[i] == file_content_lines[i]
E           assert "            ...349672458216'" == "            ...333333333334'"
E             -                             cx='-54.43333333333334'
E             +                             cx='-53.685349672458216'

tests/charts/test_charts.py:59: AssertionError
--------------------------------------------------------------------- Captured log call ----------------------------------------------------------------------
2023-12-27 20:07:16 INFO John Lennon birth location: Liverpool, 53.41058, -2.97794
2023-12-27 20:07:16 INFO John Lennon birth location: Liverpool, 53.41058, -2.97794
2023-12-27 20:07:16 INFO SVG Generated Correctly in: /Users/jack/John LennonExternalNatalChart.svg
================================================================== short test summary info ===================================================================
FAILED tests/test_kr_instance.py::TestAstrologicalSubject::test_lunar_phase - AssertionError: assert {'degrees_bet...n_emoji': '🌖'} == {'degrees_bet...un_phase': 15}
FAILED tests/charts/test_charts.py::TestCharts::test_birth_chart - assert "            ...349672458216'" == "            ...333333333334'"
FAILED tests/charts/test_charts.py::TestCharts::test_synastry_chart - assert "            ...349672458216'" == "            ...333333333334'"
FAILED tests/charts/test_charts.py::TestCharts::test_transit_chart - assert "            ...349672458216'" == "            ...333333333334'"
FAILED tests/charts/test_charts.py::TestCharts::test_external_natal_chart - assert "            ...349672458216'" == "            ...333333333334'"
================================================================ 5 failed, 61 passed in 0.36s ================================================================

I narrowed this down to the ordering of Union[int, float] in the following:

class LunarPhaseModel(BaseModel):
    degrees_between_s_m: Union[int, float]
    moon_phase: int
    sun_phase: int
    moon_emoji: LunarPhaseEmoji

Where it was casting to floats to ints in pydantic 1 since by default it was doing left_to_right union mode. Since pydantic 2 is using "smart" union it is correctly keeps it as a float. See docs here.

I changed the order of the union to make this more explicit and fixed the tests to the new (correct) svg values.

jackklika commented 8 months ago

@g-battaglia Hello, can you review this PR?

g-battaglia commented 8 months ago

Hello, thank you very much for the pull request, I've been pretty busy lately so I was not able to take a good look a the PR, I'll try to check it and merge this week. Sorry for the delay!

g-battaglia commented 8 months ago

@jackklika Ok I've just merged it and released on PyPI, since the change it's potentially breaking some dependencies the version is 4.3.0