supermoon 0.2.0__tar.gz → 0.2.2__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (26) hide show
  1. {supermoon-0.2.0 → supermoon-0.2.2}/PKG-INFO +109 -39
  2. supermoon-0.2.0/supermoon.egg-info/PKG-INFO → supermoon-0.2.2/README.md +98 -62
  3. {supermoon-0.2.0 → supermoon-0.2.2}/pyproject.toml +26 -1
  4. {supermoon-0.2.0 → supermoon-0.2.2}/supermoon/__init__.py +1 -1
  5. supermoon-0.2.2/supermoon/_util.py +10 -0
  6. {supermoon-0.2.0 → supermoon-0.2.2}/supermoon/apsis.py +4 -6
  7. {supermoon-0.2.0 → supermoon-0.2.2}/supermoon/cli.py +14 -5
  8. {supermoon-0.2.0 → supermoon-0.2.2}/supermoon/core.py +83 -59
  9. {supermoon-0.2.0 → supermoon-0.2.2}/supermoon/ephemeris.py +2 -3
  10. {supermoon-0.2.0 → supermoon-0.2.2}/supermoon/lunarphases.py +11 -13
  11. supermoon-0.2.0/README.md → supermoon-0.2.2/supermoon.egg-info/PKG-INFO +132 -34
  12. {supermoon-0.2.0 → supermoon-0.2.2}/supermoon.egg-info/SOURCES.txt +1 -0
  13. {supermoon-0.2.0 → supermoon-0.2.2}/supermoon.egg-info/requires.txt +4 -4
  14. supermoon-0.2.2/tests/oracle.py +118 -0
  15. {supermoon-0.2.0 → supermoon-0.2.2}/tests/test_apsis_phases.py +9 -7
  16. {supermoon-0.2.0 → supermoon-0.2.2}/tests/test_output.py +19 -6
  17. {supermoon-0.2.0 → supermoon-0.2.2}/tests/test_published.py +3 -2
  18. {supermoon-0.2.0 → supermoon-0.2.2}/tests/test_supermoons.py +4 -4
  19. supermoon-0.2.0/tests/oracle.py +0 -82
  20. {supermoon-0.2.0 → supermoon-0.2.2}/LICENSE +0 -0
  21. {supermoon-0.2.0 → supermoon-0.2.2}/MANIFEST.in +0 -0
  22. {supermoon-0.2.0 → supermoon-0.2.2}/setup.cfg +0 -0
  23. {supermoon-0.2.0 → supermoon-0.2.2}/supermoon/__main__.py +0 -0
  24. {supermoon-0.2.0 → supermoon-0.2.2}/supermoon.egg-info/dependency_links.txt +0 -0
  25. {supermoon-0.2.0 → supermoon-0.2.2}/supermoon.egg-info/entry_points.txt +0 -0
  26. {supermoon-0.2.0 → supermoon-0.2.2}/supermoon.egg-info/top_level.txt +0 -0
@@ -1,13 +1,20 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: supermoon
3
- Version: 0.2.0
3
+ Version: 0.2.2
4
4
  Summary: Find supermoons according to each of the popular (and conflicting) definitions
5
5
  Author-email: Tony Rice <tony@rtphokie.org>
6
6
  License-Expression: MIT
7
7
  Project-URL: Homepage, https://github.com/rtphokie/supermoon
8
+ Project-URL: Source, https://github.com/rtphokie/supermoon
8
9
  Project-URL: Issues, https://github.com/rtphokie/supermoon/issues
9
10
  Keywords: supermoon,moon,perigee,full moon,astronomy
11
+ Classifier: Development Status :: 4 - Beta
10
12
  Classifier: Programming Language :: Python :: 3
13
+ Classifier: Programming Language :: Python :: 3 :: Only
14
+ Classifier: Programming Language :: Python :: 3.11
15
+ Classifier: Programming Language :: Python :: 3.12
16
+ Classifier: Programming Language :: Python :: 3.13
17
+ Classifier: Programming Language :: Python :: 3.14
11
18
  Classifier: Operating System :: OS Independent
12
19
  Classifier: Environment :: Console
13
20
  Classifier: Intended Audience :: Science/Research
@@ -20,14 +27,17 @@ Requires-Dist: numpy
20
27
  Requires-Dist: tzlocal
21
28
  Provides-Extra: test
22
29
  Requires-Dist: pytest; extra == "test"
23
- Requires-Dist: pytz; extra == "test"
24
- Requires-Dist: requests; extra == "test"
25
- Requires-Dist: beautifulsoup4; extra == "test"
26
- Requires-Dist: lxml; extra == "test"
30
+ Provides-Extra: dev
31
+ Requires-Dist: pytest; extra == "dev"
32
+ Requires-Dist: ruff; extra == "dev"
27
33
  Dynamic: license-file
28
34
 
29
35
  # supermoon
30
36
 
37
+ [![PyPI](https://img.shields.io/pypi/v/supermoon)](https://pypi.org/project/supermoon/)
38
+ [![Python](https://img.shields.io/pypi/pyversions/supermoon)](https://pypi.org/project/supermoon/)
39
+ [![License: MIT](https://img.shields.io/pypi/l/supermoon)](https://github.com/rtphokie/supermoon/blob/master/LICENSE)
40
+
31
41
  Finds supermoons, and shows which of the popular (and conflicting) definitions each one meets.
32
42
  For background, see the [Wikipedia article on supermoons](https://en.wikipedia.org/wiki/Supermoon).
33
43
 
@@ -38,10 +48,18 @@ month, and every popular definition of it is essentially arbitrary.
38
48
 
39
49
  ## Installation
40
50
 
51
+ Requires Python 3.11 or newer.
52
+
41
53
  ```
42
54
  pip install supermoon
43
55
  ```
44
56
 
57
+ To install only the command line tool, in its own isolated environment:
58
+
59
+ ```
60
+ pipx install supermoon # or: uv tool install supermoon
61
+ ```
62
+
45
63
  The first time you run it, supermoon downloads the JPL DE421 ephemeris (about 17 MB) to
46
64
  `~/.supermoon`. To keep it somewhere else, or to reuse a copy you already have, set the
47
65
  `SUPERMOON_DATA` environment variable to that directory. DE421 covers the years 1900 through 2050.
@@ -52,12 +70,12 @@ The first time you run it, supermoon downloads the JPL DE421 ephemeris (about 17
52
70
  usage: supermoon [-h] [--cnt CNT] [-B] [-P] [-D] [-A] [-C] [year] [endyear]
53
71
 
54
72
  positional arguments:
55
- year find supermoons for this year (optional, defaults to current date forward)
56
- endyear stop finding supermoons (optional)
73
+ year find supermoons for this year (optional, defaults to now onward)
74
+ endyear last year to find supermoons for (optional)
57
75
 
58
76
  options:
59
77
  -h, --help show this help message and exit
60
- --cnt CNT moons to show
78
+ --cnt CNT moons to show (only without a year)
61
79
  -B, --brief brief output
62
80
  -P, --perigee include perigee time
63
81
  -D, --distance include distances
@@ -66,6 +84,10 @@ options:
66
84
  -C, --csv also write results to supermoons.csv
67
85
  ```
68
86
 
87
+ With no arguments, `supermoon` shows the next supermoon from now. Give a year to list every
88
+ supermoon in it, or two years to list every supermoon from the first through the second.
89
+ `--cnt` can only be used without a year.
90
+
69
91
  Examples:
70
92
 
71
93
  ```
@@ -76,27 +98,53 @@ $ supermoon 2020 2035 -B # how many supermoons there are each year from 20
76
98
  $ supermoon 2020 2035 -C # also save them to supermoons.csv
77
99
  ```
78
100
 
101
+ Sample output:
102
+
103
+ ```
104
+ $ supermoon 2025 -P -D
105
+ 3 supermoons during 2025:
106
+ Mon 10/06/2025 11:47 PM EDT (03:47 UTC) 361,456.2 km (224,598.4 mi) according to Espenak and Nolle
107
+ perigee: 10/08/2025 08:38 EDT (32.85 hours from full moon) 359,819.0 km (223,581.1 mi)
108
+ ...
109
+ ```
110
+
111
+ Times are shown in your computer's local time zone, with UTC in parentheses.
79
112
  `python -m supermoon` works the same way.
80
113
 
81
114
  ## Python usage
82
115
 
83
116
  ```python
84
- from datetime import datetime, timezone
117
+ from datetime import UTC, datetime
118
+
85
119
  import supermoon
86
120
 
87
- # the next supermoon after a date (defaults to now; naive datetimes are treated as UTC)
88
- result = supermoon.next_supermoon(datetime(2025, 1, 1, tzinfo=timezone.utc))
89
- result['fullmoon']['date'] # datetime.datetime(2025, 10, 7, 3, 47, 36, ..., tzinfo=UTC)
90
- result['fullmoon']['distance'] # km
91
- result['definitions'] # {'Sky & Telescope': False, 'Time & Date': False,
121
+ # the next supermoon on or after a date (defaults to now; naive datetimes are treated as UTC)
122
+ result = supermoon.next_supermoon(datetime(2025, 1, 1, tzinfo=UTC))
123
+ result["fullmoon"]["date"] # datetime.datetime(2025, 10, 7, 3, 47, 36, ..., tzinfo=UTC)
124
+ result["fullmoon"]["distance"] # km
125
+ result["definitions"] # {'Sky & Telescope': False, 'Time & Date': False,
92
126
  # 'Espenak': True, 'Nolle': True, 'within 1 day of perigee': False}
93
127
 
94
- supermoon.supermoons(2029) # list of every supermoon in a year
95
- supermoon.next_supermoons(count=3) # the next 3 supermoons
96
- supermoon.describe(result, perigee=True, distance=True, angulardiameter=True) # printable lines
128
+ supermoon.supermoons(2029) # list of every supermoon in a year (1900-2050)
129
+ supermoon.next_supermoons(count=3) # the next 3 supermoons from now
130
+ supermoon.next_supermoons(count=3, dt=datetime(2030, 1, 1, tzinfo=UTC))
131
+
132
+ # printable lines, the same as the command line output
133
+ for line in supermoon.describe(result, perigee=True, distance=True, angulardiameter=True):
134
+ print(line)
135
+
136
+ # save results to a CSV file
97
137
  supermoon.write_csv(supermoon.supermoons(2029), "supermoons.csv")
98
138
  ```
99
139
 
140
+ To find which supermoons meet a particular definition:
141
+
142
+ ```python
143
+ nolle = [r for r in supermoon.supermoons(2030) if r["definitions"]["Nolle"]]
144
+ ```
145
+
146
+ `supermoons()` raises `ValueError` for a year outside 1900 through 2050.
147
+
100
148
  Each result is a dictionary with these keys:
101
149
 
102
150
  | key | contents |
@@ -108,6 +156,9 @@ Each result is a dictionary with these keys:
108
156
  | `full perigee delta hours` / `full perigee delta seconds` | time between the full Moon and perigee |
109
157
  | `angular diameter` / `angular diameter raw` | the Moon's apparent size, as a string / in degrees |
110
158
 
159
+ The CSV file has the columns `fullmoon_local_date`, `perigee_local_date`, `perigee_distance_km`,
160
+ `perigee_distance_mi` and `angular_diameter` (degrees).
161
+
111
162
  ## Definitions used
112
163
 
113
164
  * Richard Nolle, an astrologer, coined the term in 1979 in an article in _Dell Horoscope_ magazine.
@@ -146,71 +197,71 @@ Times are US Eastern.
146
197
  Sun 02/09/2020 02:33 AM EST (07:33 UTC) according to Espenak
147
198
  Mon 03/09/2020 01:47 PM EDT (17:47 UTC) according to all known definitions
148
199
  Tue 04/07/2020 10:35 PM EDT (02:35 UTC) according to all known definitions
149
- Thu 05/07/2020 06:45 AM EDT (10:45 UTC) according to Espenak, and, Nolle
200
+ Thu 05/07/2020 06:45 AM EDT (10:45 UTC) according to Espenak and Nolle
150
201
  4 supermoons during 2021:
151
202
  Sun 03/28/2021 02:48 PM EDT (18:48 UTC) according to Espenak
152
203
  Mon 04/26/2021 11:31 PM EDT (03:31 UTC) according to all known definitions
153
204
  Wed 05/26/2021 07:13 AM EDT (11:13 UTC) according to all known definitions
154
- Thu 06/24/2021 02:39 PM EDT (18:39 UTC) according to Espenak, and, Nolle
205
+ Thu 06/24/2021 02:39 PM EDT (18:39 UTC) according to Espenak and Nolle
155
206
  4 supermoons during 2022:
156
- Mon 05/16/2022 12:14 AM EDT (04:14 UTC) according to Espenak, and, Nolle
207
+ Mon 05/16/2022 12:14 AM EDT (04:14 UTC) according to Espenak and Nolle
157
208
  Tue 06/14/2022 07:51 AM EDT (11:51 UTC) according to all known definitions
158
209
  Wed 07/13/2022 02:37 PM EDT (18:37 UTC) according to all known definitions
159
- Thu 08/11/2022 09:35 PM EDT (01:35 UTC) according to Espenak, and, Nolle
210
+ Thu 08/11/2022 09:35 PM EDT (01:35 UTC) according to Espenak and Nolle
160
211
  4 supermoons during 2023:
161
212
  Mon 07/03/2023 07:38 AM EDT (11:38 UTC) according to Espenak
162
213
  Tue 08/01/2023 02:31 PM EDT (18:31 UTC) according to all known definitions
163
214
  Wed 08/30/2023 09:35 PM EDT (01:35 UTC) according to all known definitions
164
- Fri 09/29/2023 05:57 AM EDT (09:57 UTC) according to Espenak, and, Nolle
215
+ Fri 09/29/2023 05:57 AM EDT (09:57 UTC) according to Espenak and Nolle
165
216
  4 supermoons during 2024:
166
217
  Mon 08/19/2024 02:25 PM EDT (18:25 UTC) according to Espenak
167
218
  Tue 09/17/2024 10:34 PM EDT (02:34 UTC) according to all known definitions
168
219
  Thu 10/17/2024 07:26 AM EDT (11:26 UTC) according to all known definitions
169
220
  Fri 11/15/2024 04:28 PM EST (21:28 UTC) according to Espenak
170
221
  3 supermoons during 2025:
171
- Mon 10/06/2025 11:47 PM EDT (03:47 UTC) according to Espenak, and, Nolle
222
+ Mon 10/06/2025 11:47 PM EDT (03:47 UTC) according to Espenak and Nolle
172
223
  Wed 11/05/2025 08:19 AM EST (13:19 UTC) according to all known definitions
173
224
  Thu 12/04/2025 06:14 PM EST (23:14 UTC) according to all known definitions
174
225
  3 supermoons during 2026:
175
226
  Sat 01/03/2026 05:02 AM EST (10:02 UTC) according to Espenak
176
- Tue 11/24/2026 09:53 AM EST (14:53 UTC) according to Espenak, and, Nolle
227
+ Tue 11/24/2026 09:53 AM EST (14:53 UTC) according to Espenak and Nolle
177
228
  Wed 12/23/2026 08:28 PM EST (01:28 UTC) according to all known definitions
178
229
  3 supermoons during 2027:
179
230
  Fri 01/22/2027 07:17 AM EST (12:17 UTC) according to all known definitions
180
231
  Sat 02/20/2027 06:23 PM EST (23:23 UTC) according to Espenak
181
232
  Mon 12/13/2027 11:08 AM EST (16:08 UTC) according to Espenak
182
233
  4 supermoons during 2028:
183
- Tue 01/11/2028 11:03 PM EST (04:03 UTC) according to Espenak, and, Nolle
234
+ Tue 01/11/2028 11:03 PM EST (04:03 UTC) according to Espenak and Nolle
184
235
  Thu 02/10/2028 10:03 AM EST (15:03 UTC) according to all known definitions
185
236
  Fri 03/10/2028 08:06 PM EST (01:06 UTC) according to all known definitions
186
237
  Sun 04/09/2028 06:26 AM EDT (10:26 UTC) according to Espenak
187
238
  5 supermoons during 2029:
188
239
  Tue 01/30/2029 01:03 AM EST (06:03 UTC) according to Espenak
189
- Wed 02/28/2029 12:10 PM EST (17:10 UTC) according to Time & Date, Espenak, and, Nolle
240
+ Wed 02/28/2029 12:10 PM EST (17:10 UTC) according to Time & Date, Espenak, and Nolle
190
241
  Thu 03/29/2029 10:26 PM EDT (02:26 UTC) according to all known definitions
191
242
  Sat 04/28/2029 06:36 AM EDT (10:36 UTC) according to all known definitions
192
243
  Sun 05/27/2029 02:37 PM EDT (18:37 UTC) according to Espenak
193
244
  5 supermoons during 2030:
194
245
  Tue 03/19/2030 01:56 PM EDT (17:56 UTC) according to Espenak
195
- Wed 04/17/2030 11:20 PM EDT (03:20 UTC) according to Time & Date, Espenak, and, Nolle
246
+ Wed 04/17/2030 11:20 PM EDT (03:20 UTC) according to Time & Date, Espenak, and Nolle
196
247
  Fri 05/17/2030 07:19 AM EDT (11:19 UTC) according to all known definitions
197
248
  Sat 06/15/2030 02:41 PM EDT (18:41 UTC) according to all known definitions
198
249
  Sun 07/14/2030 10:12 PM EDT (02:12 UTC) according to Espenak
199
250
  5 supermoons during 2031:
200
251
  Tue 05/06/2031 11:39 PM EDT (03:39 UTC) according to Espenak
201
- Thu 06/05/2031 07:58 AM EDT (11:58 UTC) according to Time & Date, Espenak, and, Nolle
252
+ Thu 06/05/2031 07:58 AM EDT (11:58 UTC) according to Time & Date, Espenak, and Nolle
202
253
  Fri 07/04/2031 03:01 PM EDT (19:01 UTC) according to all known definitions
203
254
  Sat 08/02/2031 09:45 PM EDT (01:45 UTC) according to all known definitions
204
255
  Mon 09/01/2031 05:20 AM EDT (09:20 UTC) according to Espenak
205
256
  5 supermoons during 2032:
206
257
  Wed 06/23/2032 07:32 AM EDT (11:32 UTC) according to Espenak
207
- Thu 07/22/2032 02:51 PM EDT (18:51 UTC) according to Time & Date, Espenak, Nolle, and, within 1 day of perigee
258
+ Thu 07/22/2032 02:51 PM EDT (18:51 UTC) according to Time & Date, Espenak, Nolle, and within 1 day of perigee
208
259
  Fri 08/20/2032 09:46 PM EDT (01:46 UTC) according to all known definitions
209
260
  Sun 09/19/2032 05:30 AM EDT (09:30 UTC) according to all known definitions
210
261
  Mon 10/18/2032 02:58 PM EDT (18:58 UTC) according to Espenak
211
262
  5 supermoons during 2033:
212
263
  Wed 08/10/2033 02:07 PM EDT (18:07 UTC) according to Espenak
213
- Thu 09/08/2033 10:20 PM EDT (02:20 UTC) according to Time & Date, Espenak, Nolle, and, within 1 day of perigee
264
+ Thu 09/08/2033 10:20 PM EDT (02:20 UTC) according to Time & Date, Espenak, Nolle, and within 1 day of perigee
214
265
  Sat 10/08/2033 06:58 AM EDT (10:58 UTC) according to all known definitions
215
266
  Sun 11/06/2033 03:32 PM EST (20:32 UTC) according to all known definitions
216
267
  Tue 12/06/2033 02:22 AM EST (07:22 UTC) according to Espenak
@@ -218,7 +269,7 @@ Times are US Eastern.
218
269
  Wed 09/27/2034 10:56 PM EDT (02:56 UTC) according to Espenak
219
270
  Fri 10/27/2034 08:42 AM EDT (12:42 UTC) according to all known definitions
220
271
  Sat 11/25/2034 05:32 PM EST (22:32 UTC) according to all known definitions
221
- Mon 12/25/2034 03:54 AM EST (08:54 UTC) according to Time & Date, Espenak, Nolle, and, within 1 day of perigee
272
+ Mon 12/25/2034 03:54 AM EST (08:54 UTC) according to Time & Date, Espenak, Nolle, and within 1 day of perigee
222
273
  3 supermoons during 2035:
223
274
  Tue 01/23/2035 03:16 PM EST (20:16 UTC) according to Espenak
224
275
  Thu 11/15/2035 08:48 AM EST (13:48 UTC) according to Espenak
@@ -231,18 +282,37 @@ The project uses [uv](https://docs.astral.sh/uv/):
231
282
 
232
283
  ```
233
284
  uv sync --extra test
234
- uv run pytest tests/supermoon_tests.py
285
+ uv run pytest
286
+ uv run ruff check . && uv run ruff format --check .
235
287
  uv run supermoon 2025
236
288
  ```
237
289
 
238
- `tests/basic.py` checks the perigee and apogee calculations against Fred Espenak's published
239
- tables. It downloads those tables from astropixels.com, so it needs network access.
290
+ The tests check results against an independent reference calculation (`tests/oracle.py`) and
291
+ against published values from the US Naval Observatory and Fred Espenak's perigee tables. They
292
+ need the DE421 ephemeris, which is downloaded on the first run.
240
293
 
241
294
  ## Releasing to PyPI
242
295
 
243
- ```
244
- uv build
245
- uv publish
246
- ```
296
+ 1. Bump `__version__` in `supermoon/__init__.py`.
297
+ 2. Run the tests and ruff checks above.
298
+ 3. Build and check the distributions:
299
+
300
+ ```
301
+ rm -rf dist
302
+ uv build
303
+ uvx twine check dist/*
304
+ ```
305
+
306
+ 4. Optionally, try the release on [TestPyPI](https://test.pypi.org/) first:
307
+
308
+ ```
309
+ uv publish --publish-url https://test.pypi.org/legacy/
310
+ ```
311
+
312
+ 5. Publish, then tag the release:
247
313
 
248
- Before each release, bump `__version__` in `supermoon/__init__.py`.
314
+ ```
315
+ uv publish
316
+ git tag v$(uv run python -c "import supermoon; print(supermoon.__version__)")
317
+ git push --tags
318
+ ```
@@ -1,33 +1,9 @@
1
- Metadata-Version: 2.4
2
- Name: supermoon
3
- Version: 0.2.0
4
- Summary: Find supermoons according to each of the popular (and conflicting) definitions
5
- Author-email: Tony Rice <tony@rtphokie.org>
6
- License-Expression: MIT
7
- Project-URL: Homepage, https://github.com/rtphokie/supermoon
8
- Project-URL: Issues, https://github.com/rtphokie/supermoon/issues
9
- Keywords: supermoon,moon,perigee,full moon,astronomy
10
- Classifier: Programming Language :: Python :: 3
11
- Classifier: Operating System :: OS Independent
12
- Classifier: Environment :: Console
13
- Classifier: Intended Audience :: Science/Research
14
- Classifier: Topic :: Scientific/Engineering :: Astronomy
15
- Requires-Python: >=3.11
16
- Description-Content-Type: text/markdown
17
- License-File: LICENSE
18
- Requires-Dist: skyfield>=1.31
19
- Requires-Dist: numpy
20
- Requires-Dist: tzlocal
21
- Provides-Extra: test
22
- Requires-Dist: pytest; extra == "test"
23
- Requires-Dist: pytz; extra == "test"
24
- Requires-Dist: requests; extra == "test"
25
- Requires-Dist: beautifulsoup4; extra == "test"
26
- Requires-Dist: lxml; extra == "test"
27
- Dynamic: license-file
28
-
29
1
  # supermoon
30
2
 
3
+ [![PyPI](https://img.shields.io/pypi/v/supermoon)](https://pypi.org/project/supermoon/)
4
+ [![Python](https://img.shields.io/pypi/pyversions/supermoon)](https://pypi.org/project/supermoon/)
5
+ [![License: MIT](https://img.shields.io/pypi/l/supermoon)](https://github.com/rtphokie/supermoon/blob/master/LICENSE)
6
+
31
7
  Finds supermoons, and shows which of the popular (and conflicting) definitions each one meets.
32
8
  For background, see the [Wikipedia article on supermoons](https://en.wikipedia.org/wiki/Supermoon).
33
9
 
@@ -38,10 +14,18 @@ month, and every popular definition of it is essentially arbitrary.
38
14
 
39
15
  ## Installation
40
16
 
17
+ Requires Python 3.11 or newer.
18
+
41
19
  ```
42
20
  pip install supermoon
43
21
  ```
44
22
 
23
+ To install only the command line tool, in its own isolated environment:
24
+
25
+ ```
26
+ pipx install supermoon # or: uv tool install supermoon
27
+ ```
28
+
45
29
  The first time you run it, supermoon downloads the JPL DE421 ephemeris (about 17 MB) to
46
30
  `~/.supermoon`. To keep it somewhere else, or to reuse a copy you already have, set the
47
31
  `SUPERMOON_DATA` environment variable to that directory. DE421 covers the years 1900 through 2050.
@@ -52,12 +36,12 @@ The first time you run it, supermoon downloads the JPL DE421 ephemeris (about 17
52
36
  usage: supermoon [-h] [--cnt CNT] [-B] [-P] [-D] [-A] [-C] [year] [endyear]
53
37
 
54
38
  positional arguments:
55
- year find supermoons for this year (optional, defaults to current date forward)
56
- endyear stop finding supermoons (optional)
39
+ year find supermoons for this year (optional, defaults to now onward)
40
+ endyear last year to find supermoons for (optional)
57
41
 
58
42
  options:
59
43
  -h, --help show this help message and exit
60
- --cnt CNT moons to show
44
+ --cnt CNT moons to show (only without a year)
61
45
  -B, --brief brief output
62
46
  -P, --perigee include perigee time
63
47
  -D, --distance include distances
@@ -66,6 +50,10 @@ options:
66
50
  -C, --csv also write results to supermoons.csv
67
51
  ```
68
52
 
53
+ With no arguments, `supermoon` shows the next supermoon from now. Give a year to list every
54
+ supermoon in it, or two years to list every supermoon from the first through the second.
55
+ `--cnt` can only be used without a year.
56
+
69
57
  Examples:
70
58
 
71
59
  ```
@@ -76,27 +64,53 @@ $ supermoon 2020 2035 -B # how many supermoons there are each year from 20
76
64
  $ supermoon 2020 2035 -C # also save them to supermoons.csv
77
65
  ```
78
66
 
67
+ Sample output:
68
+
69
+ ```
70
+ $ supermoon 2025 -P -D
71
+ 3 supermoons during 2025:
72
+ Mon 10/06/2025 11:47 PM EDT (03:47 UTC) 361,456.2 km (224,598.4 mi) according to Espenak and Nolle
73
+ perigee: 10/08/2025 08:38 EDT (32.85 hours from full moon) 359,819.0 km (223,581.1 mi)
74
+ ...
75
+ ```
76
+
77
+ Times are shown in your computer's local time zone, with UTC in parentheses.
79
78
  `python -m supermoon` works the same way.
80
79
 
81
80
  ## Python usage
82
81
 
83
82
  ```python
84
- from datetime import datetime, timezone
83
+ from datetime import UTC, datetime
84
+
85
85
  import supermoon
86
86
 
87
- # the next supermoon after a date (defaults to now; naive datetimes are treated as UTC)
88
- result = supermoon.next_supermoon(datetime(2025, 1, 1, tzinfo=timezone.utc))
89
- result['fullmoon']['date'] # datetime.datetime(2025, 10, 7, 3, 47, 36, ..., tzinfo=UTC)
90
- result['fullmoon']['distance'] # km
91
- result['definitions'] # {'Sky & Telescope': False, 'Time & Date': False,
87
+ # the next supermoon on or after a date (defaults to now; naive datetimes are treated as UTC)
88
+ result = supermoon.next_supermoon(datetime(2025, 1, 1, tzinfo=UTC))
89
+ result["fullmoon"]["date"] # datetime.datetime(2025, 10, 7, 3, 47, 36, ..., tzinfo=UTC)
90
+ result["fullmoon"]["distance"] # km
91
+ result["definitions"] # {'Sky & Telescope': False, 'Time & Date': False,
92
92
  # 'Espenak': True, 'Nolle': True, 'within 1 day of perigee': False}
93
93
 
94
- supermoon.supermoons(2029) # list of every supermoon in a year
95
- supermoon.next_supermoons(count=3) # the next 3 supermoons
96
- supermoon.describe(result, perigee=True, distance=True, angulardiameter=True) # printable lines
94
+ supermoon.supermoons(2029) # list of every supermoon in a year (1900-2050)
95
+ supermoon.next_supermoons(count=3) # the next 3 supermoons from now
96
+ supermoon.next_supermoons(count=3, dt=datetime(2030, 1, 1, tzinfo=UTC))
97
+
98
+ # printable lines, the same as the command line output
99
+ for line in supermoon.describe(result, perigee=True, distance=True, angulardiameter=True):
100
+ print(line)
101
+
102
+ # save results to a CSV file
97
103
  supermoon.write_csv(supermoon.supermoons(2029), "supermoons.csv")
98
104
  ```
99
105
 
106
+ To find which supermoons meet a particular definition:
107
+
108
+ ```python
109
+ nolle = [r for r in supermoon.supermoons(2030) if r["definitions"]["Nolle"]]
110
+ ```
111
+
112
+ `supermoons()` raises `ValueError` for a year outside 1900 through 2050.
113
+
100
114
  Each result is a dictionary with these keys:
101
115
 
102
116
  | key | contents |
@@ -108,6 +122,9 @@ Each result is a dictionary with these keys:
108
122
  | `full perigee delta hours` / `full perigee delta seconds` | time between the full Moon and perigee |
109
123
  | `angular diameter` / `angular diameter raw` | the Moon's apparent size, as a string / in degrees |
110
124
 
125
+ The CSV file has the columns `fullmoon_local_date`, `perigee_local_date`, `perigee_distance_km`,
126
+ `perigee_distance_mi` and `angular_diameter` (degrees).
127
+
111
128
  ## Definitions used
112
129
 
113
130
  * Richard Nolle, an astrologer, coined the term in 1979 in an article in _Dell Horoscope_ magazine.
@@ -146,71 +163,71 @@ Times are US Eastern.
146
163
  Sun 02/09/2020 02:33 AM EST (07:33 UTC) according to Espenak
147
164
  Mon 03/09/2020 01:47 PM EDT (17:47 UTC) according to all known definitions
148
165
  Tue 04/07/2020 10:35 PM EDT (02:35 UTC) according to all known definitions
149
- Thu 05/07/2020 06:45 AM EDT (10:45 UTC) according to Espenak, and, Nolle
166
+ Thu 05/07/2020 06:45 AM EDT (10:45 UTC) according to Espenak and Nolle
150
167
  4 supermoons during 2021:
151
168
  Sun 03/28/2021 02:48 PM EDT (18:48 UTC) according to Espenak
152
169
  Mon 04/26/2021 11:31 PM EDT (03:31 UTC) according to all known definitions
153
170
  Wed 05/26/2021 07:13 AM EDT (11:13 UTC) according to all known definitions
154
- Thu 06/24/2021 02:39 PM EDT (18:39 UTC) according to Espenak, and, Nolle
171
+ Thu 06/24/2021 02:39 PM EDT (18:39 UTC) according to Espenak and Nolle
155
172
  4 supermoons during 2022:
156
- Mon 05/16/2022 12:14 AM EDT (04:14 UTC) according to Espenak, and, Nolle
173
+ Mon 05/16/2022 12:14 AM EDT (04:14 UTC) according to Espenak and Nolle
157
174
  Tue 06/14/2022 07:51 AM EDT (11:51 UTC) according to all known definitions
158
175
  Wed 07/13/2022 02:37 PM EDT (18:37 UTC) according to all known definitions
159
- Thu 08/11/2022 09:35 PM EDT (01:35 UTC) according to Espenak, and, Nolle
176
+ Thu 08/11/2022 09:35 PM EDT (01:35 UTC) according to Espenak and Nolle
160
177
  4 supermoons during 2023:
161
178
  Mon 07/03/2023 07:38 AM EDT (11:38 UTC) according to Espenak
162
179
  Tue 08/01/2023 02:31 PM EDT (18:31 UTC) according to all known definitions
163
180
  Wed 08/30/2023 09:35 PM EDT (01:35 UTC) according to all known definitions
164
- Fri 09/29/2023 05:57 AM EDT (09:57 UTC) according to Espenak, and, Nolle
181
+ Fri 09/29/2023 05:57 AM EDT (09:57 UTC) according to Espenak and Nolle
165
182
  4 supermoons during 2024:
166
183
  Mon 08/19/2024 02:25 PM EDT (18:25 UTC) according to Espenak
167
184
  Tue 09/17/2024 10:34 PM EDT (02:34 UTC) according to all known definitions
168
185
  Thu 10/17/2024 07:26 AM EDT (11:26 UTC) according to all known definitions
169
186
  Fri 11/15/2024 04:28 PM EST (21:28 UTC) according to Espenak
170
187
  3 supermoons during 2025:
171
- Mon 10/06/2025 11:47 PM EDT (03:47 UTC) according to Espenak, and, Nolle
188
+ Mon 10/06/2025 11:47 PM EDT (03:47 UTC) according to Espenak and Nolle
172
189
  Wed 11/05/2025 08:19 AM EST (13:19 UTC) according to all known definitions
173
190
  Thu 12/04/2025 06:14 PM EST (23:14 UTC) according to all known definitions
174
191
  3 supermoons during 2026:
175
192
  Sat 01/03/2026 05:02 AM EST (10:02 UTC) according to Espenak
176
- Tue 11/24/2026 09:53 AM EST (14:53 UTC) according to Espenak, and, Nolle
193
+ Tue 11/24/2026 09:53 AM EST (14:53 UTC) according to Espenak and Nolle
177
194
  Wed 12/23/2026 08:28 PM EST (01:28 UTC) according to all known definitions
178
195
  3 supermoons during 2027:
179
196
  Fri 01/22/2027 07:17 AM EST (12:17 UTC) according to all known definitions
180
197
  Sat 02/20/2027 06:23 PM EST (23:23 UTC) according to Espenak
181
198
  Mon 12/13/2027 11:08 AM EST (16:08 UTC) according to Espenak
182
199
  4 supermoons during 2028:
183
- Tue 01/11/2028 11:03 PM EST (04:03 UTC) according to Espenak, and, Nolle
200
+ Tue 01/11/2028 11:03 PM EST (04:03 UTC) according to Espenak and Nolle
184
201
  Thu 02/10/2028 10:03 AM EST (15:03 UTC) according to all known definitions
185
202
  Fri 03/10/2028 08:06 PM EST (01:06 UTC) according to all known definitions
186
203
  Sun 04/09/2028 06:26 AM EDT (10:26 UTC) according to Espenak
187
204
  5 supermoons during 2029:
188
205
  Tue 01/30/2029 01:03 AM EST (06:03 UTC) according to Espenak
189
- Wed 02/28/2029 12:10 PM EST (17:10 UTC) according to Time & Date, Espenak, and, Nolle
206
+ Wed 02/28/2029 12:10 PM EST (17:10 UTC) according to Time & Date, Espenak, and Nolle
190
207
  Thu 03/29/2029 10:26 PM EDT (02:26 UTC) according to all known definitions
191
208
  Sat 04/28/2029 06:36 AM EDT (10:36 UTC) according to all known definitions
192
209
  Sun 05/27/2029 02:37 PM EDT (18:37 UTC) according to Espenak
193
210
  5 supermoons during 2030:
194
211
  Tue 03/19/2030 01:56 PM EDT (17:56 UTC) according to Espenak
195
- Wed 04/17/2030 11:20 PM EDT (03:20 UTC) according to Time & Date, Espenak, and, Nolle
212
+ Wed 04/17/2030 11:20 PM EDT (03:20 UTC) according to Time & Date, Espenak, and Nolle
196
213
  Fri 05/17/2030 07:19 AM EDT (11:19 UTC) according to all known definitions
197
214
  Sat 06/15/2030 02:41 PM EDT (18:41 UTC) according to all known definitions
198
215
  Sun 07/14/2030 10:12 PM EDT (02:12 UTC) according to Espenak
199
216
  5 supermoons during 2031:
200
217
  Tue 05/06/2031 11:39 PM EDT (03:39 UTC) according to Espenak
201
- Thu 06/05/2031 07:58 AM EDT (11:58 UTC) according to Time & Date, Espenak, and, Nolle
218
+ Thu 06/05/2031 07:58 AM EDT (11:58 UTC) according to Time & Date, Espenak, and Nolle
202
219
  Fri 07/04/2031 03:01 PM EDT (19:01 UTC) according to all known definitions
203
220
  Sat 08/02/2031 09:45 PM EDT (01:45 UTC) according to all known definitions
204
221
  Mon 09/01/2031 05:20 AM EDT (09:20 UTC) according to Espenak
205
222
  5 supermoons during 2032:
206
223
  Wed 06/23/2032 07:32 AM EDT (11:32 UTC) according to Espenak
207
- Thu 07/22/2032 02:51 PM EDT (18:51 UTC) according to Time & Date, Espenak, Nolle, and, within 1 day of perigee
224
+ Thu 07/22/2032 02:51 PM EDT (18:51 UTC) according to Time & Date, Espenak, Nolle, and within 1 day of perigee
208
225
  Fri 08/20/2032 09:46 PM EDT (01:46 UTC) according to all known definitions
209
226
  Sun 09/19/2032 05:30 AM EDT (09:30 UTC) according to all known definitions
210
227
  Mon 10/18/2032 02:58 PM EDT (18:58 UTC) according to Espenak
211
228
  5 supermoons during 2033:
212
229
  Wed 08/10/2033 02:07 PM EDT (18:07 UTC) according to Espenak
213
- Thu 09/08/2033 10:20 PM EDT (02:20 UTC) according to Time & Date, Espenak, Nolle, and, within 1 day of perigee
230
+ Thu 09/08/2033 10:20 PM EDT (02:20 UTC) according to Time & Date, Espenak, Nolle, and within 1 day of perigee
214
231
  Sat 10/08/2033 06:58 AM EDT (10:58 UTC) according to all known definitions
215
232
  Sun 11/06/2033 03:32 PM EST (20:32 UTC) according to all known definitions
216
233
  Tue 12/06/2033 02:22 AM EST (07:22 UTC) according to Espenak
@@ -218,7 +235,7 @@ Times are US Eastern.
218
235
  Wed 09/27/2034 10:56 PM EDT (02:56 UTC) according to Espenak
219
236
  Fri 10/27/2034 08:42 AM EDT (12:42 UTC) according to all known definitions
220
237
  Sat 11/25/2034 05:32 PM EST (22:32 UTC) according to all known definitions
221
- Mon 12/25/2034 03:54 AM EST (08:54 UTC) according to Time & Date, Espenak, Nolle, and, within 1 day of perigee
238
+ Mon 12/25/2034 03:54 AM EST (08:54 UTC) according to Time & Date, Espenak, Nolle, and within 1 day of perigee
222
239
  3 supermoons during 2035:
223
240
  Tue 01/23/2035 03:16 PM EST (20:16 UTC) according to Espenak
224
241
  Thu 11/15/2035 08:48 AM EST (13:48 UTC) according to Espenak
@@ -231,18 +248,37 @@ The project uses [uv](https://docs.astral.sh/uv/):
231
248
 
232
249
  ```
233
250
  uv sync --extra test
234
- uv run pytest tests/supermoon_tests.py
251
+ uv run pytest
252
+ uv run ruff check . && uv run ruff format --check .
235
253
  uv run supermoon 2025
236
254
  ```
237
255
 
238
- `tests/basic.py` checks the perigee and apogee calculations against Fred Espenak's published
239
- tables. It downloads those tables from astropixels.com, so it needs network access.
256
+ The tests check results against an independent reference calculation (`tests/oracle.py`) and
257
+ against published values from the US Naval Observatory and Fred Espenak's perigee tables. They
258
+ need the DE421 ephemeris, which is downloaded on the first run.
240
259
 
241
260
  ## Releasing to PyPI
242
261
 
243
- ```
244
- uv build
245
- uv publish
246
- ```
262
+ 1. Bump `__version__` in `supermoon/__init__.py`.
263
+ 2. Run the tests and ruff checks above.
264
+ 3. Build and check the distributions:
265
+
266
+ ```
267
+ rm -rf dist
268
+ uv build
269
+ uvx twine check dist/*
270
+ ```
271
+
272
+ 4. Optionally, try the release on [TestPyPI](https://test.pypi.org/) first:
273
+
274
+ ```
275
+ uv publish --publish-url https://test.pypi.org/legacy/
276
+ ```
277
+
278
+ 5. Publish, then tag the release:
247
279
 
248
- Before each release, bump `__version__` in `supermoon/__init__.py`.
280
+ ```
281
+ uv publish
282
+ git tag v$(uv run python -c "import supermoon; print(supermoon.__version__)")
283
+ git push --tags
284
+ ```
@@ -18,7 +18,13 @@ dependencies = [
18
18
  ]
19
19
  keywords = ["supermoon", "moon", "perigee", "full moon", "astronomy"]
20
20
  classifiers = [
21
+ "Development Status :: 4 - Beta",
21
22
  "Programming Language :: Python :: 3",
23
+ "Programming Language :: Python :: 3 :: Only",
24
+ "Programming Language :: Python :: 3.11",
25
+ "Programming Language :: Python :: 3.12",
26
+ "Programming Language :: Python :: 3.13",
27
+ "Programming Language :: Python :: 3.14",
22
28
  "Operating System :: OS Independent",
23
29
  "Environment :: Console",
24
30
  "Intended Audience :: Science/Research",
@@ -26,10 +32,12 @@ classifiers = [
26
32
  ]
27
33
 
28
34
  [project.optional-dependencies]
29
- test = ["pytest", "pytz", "requests", "beautifulsoup4", "lxml"]
35
+ test = ["pytest"]
36
+ dev = ["pytest", "ruff"]
30
37
 
31
38
  [project.urls]
32
39
  Homepage = "https://github.com/rtphokie/supermoon"
40
+ Source = "https://github.com/rtphokie/supermoon"
33
41
  Issues = "https://github.com/rtphokie/supermoon/issues"
34
42
 
35
43
  [project.scripts]
@@ -46,3 +54,20 @@ testpaths = ["tests"]
46
54
 
47
55
  [tool.ruff.format]
48
56
  exclude = ["*.md"]
57
+
58
+ [tool.ruff.lint]
59
+ select = [
60
+ "E", "W", # pycodestyle
61
+ "F", # pyflakes
62
+ "I", # isort
63
+ "B", # flake8-bugbear
64
+ "UP", # pyupgrade
65
+ "SIM", # flake8-simplify
66
+ "C4", # flake8-comprehensions
67
+ "PIE", # flake8-pie
68
+ "RET", # flake8-return
69
+ "PTH", # flake8-use-pathlib
70
+ "PT", # flake8-pytest-style
71
+ "N", # pep8-naming
72
+ "RUF", # ruff-specific
73
+ ]
@@ -5,5 +5,5 @@ from .core import describe, next_supermoon, next_supermoons, supermoons, write_c
5
5
  name = "supermoon"
6
6
  __author__ = """Tony Rice"""
7
7
  __email__ = "tony@rtphokie.org"
8
- __version__ = "0.2.0"
8
+ __version__ = "0.2.2"
9
9
  __all__ = ["describe", "next_supermoon", "next_supermoons", "supermoons", "write_csv"]