From d7def507034e6af0d7c7bdbc4d4174b046b082ac Mon Sep 17 00:00:00 2001
From: Deuchnord <git@deuchnord.fr>
Date: Sun, 16 Mar 2025 11:34:50 +0100
Subject: [PATCH] fix: Python 3.13 support

---
 .github/workflows/tests.yml                |   1 +
 kosmorro/locales/messages.pot              |  57 ++++---
 tests/general.py                           |  54 +++++-
 tests/outputs/2020-01-27-with-position.tex | 181 +++++++++++++++++++++
 4 files changed, 266 insertions(+), 27 deletions(-)
 create mode 100644 tests/outputs/2020-01-27-with-position.tex

diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml
index 7d9ed7d..241d918 100644
--- a/.github/workflows/tests.yml
+++ b/.github/workflows/tests.yml
@@ -25,6 +25,7 @@ jobs:
           - '3.10'
           - '3.11'
           - '3.12'
+          - '3.13'
 
     name: Python ${{ matrix.python-version }} on ${{ matrix.os }}
     steps:
diff --git a/kosmorro/locales/messages.pot b/kosmorro/locales/messages.pot
index a66c397..c89367e 100644
--- a/kosmorro/locales/messages.pot
+++ b/kosmorro/locales/messages.pot
@@ -8,29 +8,30 @@ msgid ""
 msgstr ""
 "Project-Id-Version: PROJECT VERSION\n"
 "Report-Msgid-Bugs-To: EMAIL@ADDRESS\n"
-"POT-Creation-Date: 2025-03-16 11:24+0100\n"
+"POT-Creation-Date: 2025-03-16 11:59+0100\n"
 "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
 "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
 "Language-Team: LANGUAGE <LL@li.org>\n"
 "MIME-Version: 1.0\n"
 "Content-Type: text/plain; charset=utf-8\n"
 "Content-Transfer-Encoding: 8bit\n"
-"Generated-By: Babel 2.15.0\n"
+"Generated-By: Babel 2.17.0\n"
 
-#: kosmorro/__main__.py:61
+#: kosmorro/__main__.py:62
 msgid ""
 "Save the planet and paper!\n"
 "Consider printing your PDF document only if really necessary, and use the"
 " other side of the sheet."
 msgstr ""
 
-#: kosmorro/__main__.py:70
+#: kosmorro/__main__.py:71
 msgid ""
 "PDF output will not contain the ephemerides, because you didn't provide "
 "the observation coordinates."
 msgstr ""
 
 #: kosmorro/__main__.py:115
+#, python-brace-format
 msgid "The file could not be saved in \"{path}\": {error}"
 msgstr ""
 
@@ -38,21 +39,23 @@ msgstr ""
 msgid "Please provide a file path to export in this format (--output)."
 msgstr ""
 
-#: kosmorro/__main__.py:162
+#: kosmorro/__main__.py:163
+#, python-brace-format
 msgid "Moon phase can only be computed between {min_date} and {max_date}"
 msgstr ""
 
-#: kosmorro/__main__.py:201
+#: kosmorro/__main__.py:202
+#, python-brace-format
 msgid "Running on Python {python_version} with Kosmorrolib v{kosmorrolib_version}"
 msgstr ""
 
-#: kosmorro/__main__.py:214
+#: kosmorro/__main__.py:215
 msgid ""
 "Compute the ephemerides and the events for a given date and a given "
 "position on Earth."
 msgstr ""
 
-#: kosmorro/__main__.py:217
+#: kosmorro/__main__.py:218
 msgid ""
 "By default, only the events will be computed for today.\n"
 "To compute also the ephemerides, latitude and longitude arguments are "
@@ -67,26 +70,26 @@ msgstr ""
 msgid "The format to output the information to"
 msgstr ""
 
-#: kosmorro/__main__.py:245
+#: kosmorro/__main__.py:246
 msgid ""
 "The observer's latitude on Earth. Can also be set in the "
 "KOSMORRO_LATITUDE environment variable."
 msgstr ""
 
-#: kosmorro/__main__.py:255
+#: kosmorro/__main__.py:256
 msgid ""
 "The observer's longitude on Earth. Can also be set in the "
 "KOSMORRO_LONGITUDE environment variable."
 msgstr ""
 
-#: kosmorro/__main__.py:265
+#: kosmorro/__main__.py:266
 msgid ""
 "The date for which the ephemerides must be calculated. Can be in the "
 "YYYY-MM-DD format or an interval in the \"[+-]YyMmDd\" format (with Y, M,"
 " and D numbers). Defaults to current date."
 msgstr ""
 
-#: kosmorro/__main__.py:276
+#: kosmorro/__main__.py:277
 msgid ""
 "The timezone to display the hours in (e.g. 2 for UTC+2 or -3 for UTC-3). "
 "Can also be set in the KOSMORRO_TIMEZONE environment variable."
@@ -96,13 +99,13 @@ msgstr ""
 msgid "Disable the colors in the console."
 msgstr ""
 
-#: kosmorro/__main__.py:292
+#: kosmorro/__main__.py:293
 msgid ""
 "A file to export the output to. If not given, the standard output is "
 "used. This argument is needed for PDF format."
 msgstr ""
 
-#: kosmorro/__main__.py:301
+#: kosmorro/__main__.py:302
 msgid ""
 "Do not generate a graph to represent the rise and set times in the PDF "
 "format."
@@ -113,10 +116,12 @@ msgid "Show debugging messages"
 msgstr ""
 
 #: kosmorro/date.py:17
+#, python-brace-format
 msgid "The date {date} is not valid: {error}"
 msgstr ""
 
-#: kosmorro/date.py:39
+#: kosmorro/date.py:40
+#, python-brace-format
 msgid ""
 "The date {date} does not match the required YYYY-MM-DD format or the "
 "offset format."
@@ -130,7 +135,8 @@ msgstr ""
 msgid "Note: All the hours are given in UTC."
 msgstr ""
 
-#: kosmorro/dumper.py:151
+#: kosmorro/dumper.py:152
+#, python-brace-format
 msgid "Note: All the hours are given in the UTC{offset} timezone."
 msgstr ""
 
@@ -158,7 +164,8 @@ msgstr ""
 msgid "Moon phase:"
 msgstr ""
 
-#: kosmorro/dumper.py:242
+#: kosmorro/dumper.py:243
+#, python-brace-format
 msgid "{next_moon_phase} on {next_moon_phase_date} at {next_moon_phase_time}"
 msgstr ""
 
@@ -166,14 +173,15 @@ msgstr ""
 msgid "Overview of your sky"
 msgstr ""
 
-#: kosmorro/dumper.py:314
+#: kosmorro/dumper.py:315
+#, python-brace-format
 msgid ""
 "This document summarizes the ephemerides and the events of {date}. It "
 "aims to help you to prepare your observation session. All the hours are "
 "given in {timezone}."
 msgstr ""
 
-#: kosmorro/dumper.py:324
+#: kosmorro/dumper.py:325
 msgid ""
 "Don't forget to check the weather forecast before you go out with your "
 "equipment."
@@ -191,7 +199,7 @@ msgstr ""
 msgid "Expected events"
 msgstr ""
 
-#: kosmorro/dumper.py:487
+#: kosmorro/dumper.py:488
 msgid ""
 "Building PDF was not possible, because some dependencies are not "
 "installed.\n"
@@ -199,7 +207,7 @@ msgid ""
 "pdf/ for more information."
 msgstr ""
 
-#: kosmorro/dumper.py:540
+#: kosmorro/dumper.py:541
 #, python-format
 msgid ""
 "An error occurred during the compilation of the PDF.\n"
@@ -207,7 +215,8 @@ msgid ""
 "share the content of the log file at /tmp/kosmorro-%s.log"
 msgstr ""
 
-#: kosmorro/exceptions.py:35
+#: kosmorro/exceptions.py:36
+#, python-brace-format
 msgid "The date must be between {minimum_date} and {maximum_date}"
 msgstr ""
 
@@ -318,18 +327,22 @@ msgid "Pluto"
 msgstr ""
 
 #: kosmorro/i18n/utils.py:27
+#, python-brace-format
 msgid "{day_of_week} {month} {day_number}, {year}"
 msgstr ""
 
 #: kosmorro/i18n/utils.py:30
+#, python-brace-format
 msgid "{month} {day_number}, {hours}:{minutes}"
 msgstr ""
 
 #: kosmorro/i18n/utils.py:33
+#, python-brace-format
 msgid "{month} {day_number}, {year}"
 msgstr ""
 
 #: kosmorro/i18n/utils.py:36
+#, python-brace-format
 msgid "{hours}:{minutes}"
 msgstr ""
 
diff --git a/tests/general.py b/tests/general.py
index 33971dd..5e38b84 100644
--- a/tests/general.py
+++ b/tests/general.py
@@ -33,6 +33,7 @@ def test_run_without_argument():
 def test_help_message():
     for arg in ["--help", "-h"]:
         result = execute(KOSMORRO + [arg])
+
         assert result.is_successful()
 
         # Options header has changed from "optional arguments" to "options" in Python 3.10.
@@ -40,9 +41,10 @@ def test_help_message():
             "optional arguments" if python_version.minor < 10 else "options"
         )
 
-        assert (
-            result.stdout
-            == """usage: kosmorro [-h] [--version] [--format {text,json,pdf}]
+        if python_version.major == 3 and python_version.minor < 13:
+            assert (
+                result.stdout
+                == """usage: kosmorro [-h] [--version] [--format {text,json,pdf}]
                 [--latitude LATITUDE] [--longitude LONGITUDE] [--date DATE]
                 [--timezone TIMEZONE] [--no-colors] [--output OUTPUT]
                 [--no-graph] [--debug]
@@ -81,5 +83,47 @@ on Earth.
 By default, only the events will be computed for today. To compute also the
 ephemerides, latitude and longitude arguments are needed.
 """
-            % options_header
-        )
+                % options_header
+            )
+        else:
+            assert (
+                result.stdout
+                == """usage: kosmorro [-h] [--version] [--format {text,json,pdf}]
+                [--latitude LATITUDE] [--longitude LONGITUDE] [--date DATE]
+                [--timezone TIMEZONE] [--no-colors] [--output OUTPUT]
+                [--no-graph] [--debug]
+
+Compute the ephemerides and the events for a given date and a given position
+on Earth.
+
+options:
+  -h, --help            show this help message and exit
+  --version, -v         Show the program version
+  --format, -f {text,json,pdf}
+                        The format to output the information to
+  --latitude, -lat LATITUDE
+                        The observer's latitude on Earth. Can also be set in
+                        the KOSMORRO_LATITUDE environment variable.
+  --longitude, -lon LONGITUDE
+                        The observer's longitude on Earth. Can also be set in
+                        the KOSMORRO_LONGITUDE environment variable.
+  --date, -d DATE       The date for which the ephemerides must be calculated.
+                        Can be in the YYYY-MM-DD format or an interval in the
+                        "[+-]YyMmDd" format (with Y, M, and D numbers).
+                        Defaults to current date.
+  --timezone, -t TIMEZONE
+                        The timezone to display the hours in (e.g. 2 for UTC+2
+                        or -3 for UTC-3). Can also be set in the
+                        KOSMORRO_TIMEZONE environment variable.
+  --no-colors           Disable the colors in the console.
+  --output, -o OUTPUT   A file to export the output to. If not given, the
+                        standard output is used. This argument is needed for
+                        PDF format.
+  --no-graph            Do not generate a graph to represent the rise and set
+                        times in the PDF format.
+  --debug               Show debugging messages
+
+By default, only the events will be computed for today. To compute also the
+ephemerides, latitude and longitude arguments are needed.
+"""
+            )
diff --git a/tests/outputs/2020-01-27-with-position.tex b/tests/outputs/2020-01-27-with-position.tex
new file mode 100644
index 0000000..afd5940
--- /dev/null
+++ b/tests/outputs/2020-01-27-with-position.tex
@@ -0,0 +1,181 @@
+\documentclass[a4paper,12pt]{article}
+
+\usepackage[utf8]{inputenc}
+\usepackage[T1]{fontenc}
+\usepackage[margin=25mm]{geometry}
+\usepackage{graphicx}
+\usepackage{hyperref}
+\usepackage{xcolor}
+\usepackage{fp}
+
+% Command showing the Moon phase
+\newcommand{\moonphase}[2]{
+    \begin{center}
+        \begin{minipage}{2cm}
+            \includegraphics[width=\linewidth]{#1}
+        \end{minipage}
+        \hspace{5mm}
+        \begin{minipage}{7cm}
+            \textbf{\currentmoonphasetitle}\\#2
+        \end{minipage}
+    \end{center}
+}
+
+% Environment for the ephemerides, when --no-graph is given on the command line
+\newenvironment{ephemerides}{
+    \begin{table}[h]
+        \centering
+        \begin{tabular}{lccc}
+            \textbf{\ephemeridesobjecttitle} &
+            \textbf{\ephemeridesrisetimetitle} &
+            \textbf{\ephemeridesculminationtimetitle} &
+            \textbf{\ephemeridessettimetitle}\\
+\hline
+}{
+        \end{tabular}
+    \end{table}
+}
+
+% Command adding an object to the ephemerides environment
+\newcommand{\object}[4]{
+    \hline
+    \textbf{#1} & {#2} & {#3} & {#4}\\
+}
+
+% Environment to insert the ephemerides graph
+\newenvironment{graphephemerides}{\setlength{\unitlength}{0.02\linewidth}
+\begin{picture}(20,20)
+    % Axes
+    \put(0,-2){\vector(1,0){50}}
+    \multiput(0,-2)(2,0){24}{
+        \line(0,-1){0.25}
+    }
+    \newcounter{hour}
+    \multiput(-0.25,-3.5)(4,0){12}{
+        \sffamily\footnotesize
+        \arabic{hour}\stepcounter{hour}\stepcounter{hour}
+    }
+    \put(49,-3.5){\sffamily\footnotesize \hourslabel}
+
+    % Graduation
+
+    \put(50,-0.5){\sffamily\footnotesize \Pluto}
+    \put(50,1.5){\sffamily\footnotesize \Neptune}
+    \put(50,3.5){\sffamily\footnotesize \Uranus}
+    \put(50,5.5){\sffamily\footnotesize \Saturn}
+    \put(50,7.5){\sffamily\footnotesize \Jupiter}
+    \put(50,9.5){\sffamily\footnotesize \Mars}
+    \put(50,11.5){\sffamily\footnotesize \Venus}
+    \put(50,13.5){\sffamily\footnotesize \Mercury}
+    \put(50,15.5){\sffamily\footnotesize \Moon}
+    \put(50,17.5){\sffamily\footnotesize \Sun}
+
+    \multiput(0,0)(0,2){10}{
+        \color{gray}\line(1,0){48}
+    }
+
+    \linethickness{1.5mm}
+}{
+\end{picture}
+\vspace{1cm}
+}
+
+% Command to add an object to the graph
+\newcommand{\graphobject}[8]{%
+    % #1: Y coordinate component
+    % #2: Color
+    % #3: Hour rise time
+    % #4: Minute rise time
+    % #5: Hour set time
+    % #6: Minute set time
+    % #7: Human-readable rise time
+    % #8: Human-readable set time
+
+    \FPeval{\start}{#3*2+(#4/60)*2}%
+    \FPeval{\length}{#5*2+(#6/60)*2 - \start}%
+    \FPeval{\starttext}{\start+0.7}%
+    \FPeval{\endtext}{\start+\length-3.25}%
+
+    {\color{#2}%
+    \put(\start,#1){%
+        \line(1, 0){\length}%
+    }}%
+
+    \put(\starttext,#1.5){\sffamily\footnotesize #7}%
+    \put(\endtext,#1.5){\sffamily\footnotesize #8}%
+}
+
+\newcommand{\event}[2]{
+    \textbf{#1} & {#2}\\
+}
+
+\newenvironment{events}{
+    \begin{table}[h]
+        \begin{tabular}{ll}
+}{
+        \end{tabular}
+    \end{table}
+}
+
+% Commands to handle the translated strings
+\newcommand{\currentmoonphasetitle}{Moon phase:}
+\newcommand{\ephemeridesobjecttitle}{Object}
+\newcommand{\ephemeridesrisetimetitle}{Rise time}
+\newcommand{\ephemeridesculminationtimetitle}{Culmination time}
+\newcommand{\ephemeridessettimetitle}{Set time}
+\newcommand{\hourslabel}{hours}
+
+\newcommand{\Pluto}{Pluto}
+\newcommand{\Neptune}{Neptune}
+\newcommand{\Uranus}{Uranus}
+\newcommand{\Saturn}{Saturn}
+\newcommand{\Jupiter}{Jupiter}
+\newcommand{\Mars}{Mars}
+\newcommand{\Venus}{Venus}
+\newcommand{\Mercury}{Mercury}
+\newcommand{\Moon}{Moon}
+\newcommand{\Sun}{Sun}
+
+% Fix Unicode issues
+\DeclareUnicodeCharacter{202F}{~}
+\DeclareUnicodeCharacter{00B0}{$^\circ$}
+
+\hypersetup{pdfinfo={%
+    Title={Overview of your sky},
+    Creator={Kosmorro v__APP_VERSION__}
+}}
+
+\pagenumbering{gobble}
+\setcounter{secnumdepth}{0}
+
+\title{\sffamily\href{http://kosmorro.space}{\includegraphics[width=5cm]{__PROJECT_PATH__/assets/png/kosmorro-logo.png}}\\Overview of your sky}
+\date{\vspace{-11mm}\sffamily Monday, January 27, 2020}
+
+\begin{document}
+
+    \maketitle
+
+    This document summarizes the ephemerides and the events of Monday, January 27, 2020. It aims to help you to prepare your observation session. All the hours are given in UTC.
+
+Don't forget to check the weather forecast before you go out with your equipment.
+
+    \moonphase{__PROJECT_PATH__/assets/moonphases/png/new-moon.png}{New Moon}
+
+    %%% BEGIN-EPHEMERIDES-SECTION
+    \section{\sffamily Ephemerides of the day}
+
+    \begin{graphephemerides}%
+\graphobject{18}{gray}{7}{31}{16}{30}{7:31 AM}{4:30 PM}\graphobject{16}{gray}{9}{6}{19}{13}{9:06 AM}{7:13 PM}\graphobject{14}{gray}{8}{10}{17}{28}{8:10 AM}{5:28 PM}\graphobject{12}{gray}{9}{1}{20}{10}{9:01 AM}{8:10 PM}\graphobject{10}{gray}{4}{19}{12}{28}{4:19 AM}{12:28 PM}\graphobject{8}{gray}{6}{15}{14}{21}{6:15 AM}{2:21 PM}\graphobject{6}{gray}{6}{56}{15}{22}{6:56 AM}{3:22 PM}\graphobject{4}{gray}{0}{0}{0}{33}{}{12:33 AM}\graphobject{4}{gray}{10}{21}{24}{0}{10:21 AM}{}\graphobject{2}{gray}{9}{1}{20}{10}{9:01 AM}{8:10 PM}\graphobject{0}{gray}{6}{57}{15}{11}{6:57 AM}{3:11 PM}
+    \end{graphephemerides}
+    %%% END-EPHEMERIDES-SECTION
+
+    %%% BEGIN-EVENTS-SECTION
+    \section{\sffamily Expected events}
+
+    \begin{events}
+        \event{8:00 PM}{Venus and Neptune are in conjunction}
+    \end{events}
+    %%% END-EVENTS-SECTION
+
+\end{document}
+