Skip to content

Commit 6a248de

Browse files
committed
Overlay config setting dicts; add Python 3.13; linter fixes
1 parent 8a7c6d9 commit 6a248de

File tree

14 files changed

+117
-95
lines changed

14 files changed

+117
-95
lines changed

.github/workflows/test.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ jobs:
55
runs-on: ubuntu-latest
66
strategy:
77
matrix:
8-
python-version: ["3.10", "3.11", "3.12"]
8+
python-version: ["3.10", "3.11", "3.12", "3.13"]
99
steps:
1010
- uses: actions/checkout@v4
1111
- name: Set up Python ${{ matrix.python-version }}

Justfile

Lines changed: 17 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -26,26 +26,23 @@ update:
2626
{{PIP}} install -U \
2727
build \
2828
pytest \
29-
docutils \
3029
pytest-sugar \
3130
pytest-clarity \
3231
freezegun \
3332
responses \
3433
coverage \
3534
tox \
3635
ipython \
37-
flake8 \
38-
black \
3936
twine \
40-
bump \
37+
ruff \
4138
isort
4239

4340
# Create a virtual environment if needed
4441
venv:
4542
#!/usr/bin/env bash
4643
if [ ! -d {{VENV}} ]; then
4744
echo Creating virtual env in dir {{VENV}} ...
48-
python3 -m venv {{VENV}}
45+
python3 -m venv --prompt when {{VENV}}
4946
fi
5047

5148
# Create virtual environment and install / update all dev dependencies
@@ -103,13 +100,22 @@ purge: clean clean-dev rmvenv clean-build
103100
build:
104101
{{BIN}}/python -m build --outdir ./.dev/dist
105102

103+
# Run linter and code formatter checks
104+
check:
105+
@echo Linting...
106+
-{{BIN}}/ruff check --diff src/when tests
107+
108+
@echo Format checks...
109+
-{{BIN}}/ruff format --diff --line-length 100 src/when tests
110+
106111
# Run linter and code formatter tools
107112
lint:
108113
@echo Linting...
109-
-{{BIN}}/flake8 src/when tests
114+
-{{BIN}}/ruff check src/when tests
110115

111116
@echo Format checks...
112-
-{{BIN}}/black --check --diff -l 100 src/when tests
117+
-{{BIN}}/ruff format --line-length 100 src/when tests
118+
113119

114120
# Launch sqlite data browser (macOS only)
115121
[macos]
@@ -133,6 +139,10 @@ strftime:
133139
"Notes:\n* - Locale-dependent\n+ - C99 extension\n! - when extension"
134140
)
135141

142+
# Run a command from the venv bin directory
143+
run *args:
144+
{{BIN}}/"$@"
145+
136146
# Execute the when command with any arbitrary arguments
137147
when *args:
138148
@{{BIN}}/when "$@"

README.md

Lines changed: 27 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4,12 +4,34 @@
44
[![PyPI](https://img.shields.io/pypi/v/when.svg)](https://pypi.python.org/pypi/when)
55

66
**Scenario:** Your favorite sporting event, concert, performance, conference, or symposium is happening
7-
in Ulaanbaatar, Mongolia and all you know is the time of the event relative to the location.
7+
in Ulan Bator, Mongolia and all you have is the time of the event relative to the location -- Feb 8, 3pm.
8+
9+
* What time is it currently in Ulan Bator?
10+
11+
```console
12+
$ when --source "Ulan Bator"
13+
2025-02-07 03:08:58+0800 (+08, Asia/Ulaanbaatar) 038d05w (Ulan Bator, Ulaanbaatar, MN, Asia/Ulaanbaatar)[🌓 First Quarter]
14+
```
15+
* What time is the event in your local time (PST for me, currently)?
16+
17+
```console
18+
$ when --source "Ulan Bator" Feb 8 3pm
19+
2025-02-07 23:00:00-0800 (PST, America/Los_Angeles) 038d05w [🌓 First Quarter]
20+
```
821

9-
* So what time is that for you in your local time?
1022
* What time did it or will it occur at some other time, past or present?
23+
24+
```console
25+
$ when --source "Ulan Bator" --offset +15d6h
26+
2025-02-22 09:18:01+0800 (+08, Asia/Ulaanbaatar) 053d07w (Ulan Bator, Ulaanbaatar, MN, Asia/Ulaanbaatar)[🌗 Last Quarter]
27+
```
1128
* What about for your friends in other locations around the world?
1229

30+
```console
31+
$ when --exact --target London,GB --source "Ulan Bator" Feb 8 3pm
32+
2025-02-08 07:00:00+0000 (GMT, Europe/London) 039d05w (London, England, GB, Europe/London)[🌓 First Quarter]
33+
```
34+
1335
## Table of Contents
1436

1537
- [Features](#features)
@@ -548,7 +570,7 @@ $ when --config
548570

549571
You can refer to [Default TOML](#default-toml) below for all configuration values.
550572

551-
To begin, lets create the file:
573+
To begin, create the file:
552574

553575
```console
554576
$ cat << EOF > .whenrc.toml
@@ -561,13 +583,13 @@ grouped = " ➡️ From "
561583
EOF
562584
```
563585

564-
Now, verify your configuration by doing:
586+
Now, verify the configuration by doing:
565587

566588
```console
567589
$ when --config
568590
```
569591

570-
At the top of the output, you should see a line similar to the following:
592+
At the top of the output there should be a line similar to the following:
571593

572594
```console
573595
# Read from /path/to/pwd/.whenrc.toml

pyproject.toml

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -22,9 +22,10 @@ classifiers = [
2222
"Operating System :: MacOS :: MacOS X",
2323
"Programming Language :: Python",
2424
"Programming Language :: Python :: 3",
25-
"Programming Language :: Python :: 3.8",
26-
"Programming Language :: Python :: 3.9",
2725
"Programming Language :: Python :: 3.10",
26+
"Programming Language :: Python :: 3.11",
27+
"Programming Language :: Python :: 3.12",
28+
"Programming Language :: Python :: 3.13",
2829
"Topic :: Software Development",
2930
"Topic :: Software Development :: Libraries",
3031
"Topic :: Software Development :: Libraries :: Python Modules",
@@ -51,8 +52,13 @@ packages = ["when", "when.db"]
5152
[tool.setuptools.dynamic]
5253
version = { attr = "when.__version__"}
5354

54-
[tool.black]
55+
[tool.ruff]
56+
cache-dir = ".dev/ruff"
5557
line-length = 100
58+
indent-width = 4
59+
60+
[tool.ruff.lint]
61+
ignore = ["E741"]
5662

5763
[tool.pytest.ini_options]
5864
cache_dir = "./.dev/pytest_cache"

src/when/__init__.py

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,7 @@
99
def __getattr__(name):
1010
if name == "when":
1111
from .core import When
12-
from .config import Settings
1312

14-
return When(Settings())
13+
return When()
1514

1615
raise AttributeError(f"What is {name}?")

src/when/cli.py

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -176,10 +176,7 @@ def get_parser(settings):
176176
)
177177

178178
parser.add_argument(
179-
"--alias",
180-
type=int,
181-
dest="db_alias",
182-
help="Create a new alias from the city id"
179+
"--alias", type=int, dest="db_alias", help="Create a new alias from the city id"
183180
)
184181

185182
parser.add_argument(

src/when/config.py

Lines changed: 27 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -222,31 +222,6 @@
222222
"""
223223

224224

225-
def overlay(first, other):
226-
if not isinstance(other, dict):
227-
return first
228-
229-
keys = set([*first.keys(), *other.keys()])
230-
result = {}
231-
for key in keys:
232-
if key not in other:
233-
result[key] = first[key]
234-
continue
235-
236-
if key not in first:
237-
result[key] = other[key]
238-
continue
239-
240-
if isinstance(first[key], dict):
241-
result[key] = overlay(first[key], other[key])
242-
continue
243-
244-
result[key] = other[key]
245-
246-
return result
247-
248-
249-
250225
class Settings:
251226
NAME = ".whenrc.toml"
252227
DIRS = [Path.cwd(), Path.home()]
@@ -269,14 +244,14 @@ def read_path(self, path):
269244
except FileNotFoundError:
270245
pass
271246
else:
272-
self.read_from.append(path)
273247
try:
274-
result = overlay(self.data, data)
248+
result = Settings.overlay(self.data, data)
275249
except Exception as why:
276-
print(f"Unable to settings file {path}, skipping:", file=sys.stderr)
250+
print(f"Unable to import settings file {path}, skipping:", file=sys.stderr)
277251
print(f"{why}", file=sys.stderr)
278252
else:
279253
self.data = result
254+
self.read_from.append(path)
280255

281256
@property
282257
def paths(self):
@@ -289,3 +264,27 @@ def write_text(self):
289264

290265
out = toml.dumps(self.data)
291266
return f"{text}{out}"
267+
268+
@staticmethod
269+
def overlay(first, other):
270+
if not isinstance(other, dict):
271+
return first
272+
273+
keys = set([*first.keys(), *other.keys()])
274+
result = {}
275+
for key in keys:
276+
if key not in other:
277+
result[key] = first[key]
278+
continue
279+
280+
if key not in first:
281+
result[key] = other[key]
282+
continue
283+
284+
if isinstance(first[key], dict):
285+
result[key] = Settings.overlay(first[key], other[key])
286+
continue
287+
288+
result[key] = other[key]
289+
290+
return result

src/when/core.py

Lines changed: 7 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,13 @@
11
import fnmatch
22
import json
3-
import logging
43
import re
54
from datetime import date, datetime, timedelta
65
from itertools import chain
76

87
from dateutil import rrule
98
from dateutil.easter import easter
109

11-
from . import exceptions, timezones, utils
12-
from .config import DEFAULT_FORMAT, FORMAT_SPECIFIERS
10+
from . import exceptions, timezones, utils, config
1311
from .db import client
1412
from .lunar import lunar_phase
1513

@@ -72,9 +70,9 @@ def __init__(self, settings, format="default", delta=None):
7270
self.settings = settings
7371
self.format = self.settings["formats"]["named"].get(format, format)
7472

75-
self.c99_specs = [fs[0][1] for fs in FORMAT_SPECIFIERS if "+" in fs[-1]]
76-
self.when_specs = [fs[0][2] for fs in FORMAT_SPECIFIERS if "!" == fs[-1]]
77-
self.cond_specs = [fs[0][2] for fs in FORMAT_SPECIFIERS if "!!" == fs[-1]]
73+
self.c99_specs = [fs[0][1] for fs in config.FORMAT_SPECIFIERS if "+" in fs[-1]]
74+
self.when_specs = [fs[0][2] for fs in config.FORMAT_SPECIFIERS if "!" == fs[-1]]
75+
self.cond_specs = [fs[0][2] for fs in config.FORMAT_SPECIFIERS if "!!" == fs[-1]]
7876
self.delta = delta
7977

8078
def token_replacement(self, result, value, pattern, specs, prefix):
@@ -149,7 +147,7 @@ def c99_F(self, result):
149147

150148
def c99_g(self, result):
151149
"Week-based year, last two digits (00-99): 01"
152-
return f"{result.dt.year%100:02}"
150+
return f"{result.dt.year % 100:02}"
153151

154152
def c99_G(self, result):
155153
"Week-based year: 2001"
@@ -255,8 +253,8 @@ def __repr__(self):
255253

256254

257255
class When:
258-
def __init__(self, settings, local_zone=None, db=None):
259-
self.settings = settings
256+
def __init__(self, settings=None, local_zone=None, db=None):
257+
self.settings = settings or config.Settings()
260258
self.db = db or client.DB()
261259
self.tz_dict = {z: z for z in utils.all_zones()}
262260
for key in list(self.tz_dict):

src/when/db/__init__.py

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
import sys
22

33
from . import make, client
4-
from .. import utils
54

65
CITY_FILE_SIZES = make.CITY_FILE_SIZES
76

src/when/db/client.py

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,9 @@
11
import os
22
import re
3-
import contextlib
4-
import logging
53
import sqlite3
6-
from collections import namedtuple
4+
import contextlib
75
from pathlib import Path
6+
from collections import namedtuple
87

98
from .. import utils
109
from ..exceptions import DBError

0 commit comments

Comments
 (0)