Skip to content

Commit

Permalink
Overlay config setting dicts; add Python 3.13; linter fixes
Browse files Browse the repository at this point in the history
  • Loading branch information
dakrauth committed Feb 7, 2025
1 parent 8a7c6d9 commit 6a248de
Show file tree
Hide file tree
Showing 14 changed files with 117 additions and 95 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ jobs:
runs-on: ubuntu-latest
strategy:
matrix:
python-version: ["3.10", "3.11", "3.12"]
python-version: ["3.10", "3.11", "3.12", "3.13"]
steps:
- uses: actions/checkout@v4
- name: Set up Python ${{ matrix.python-version }}
Expand Down
24 changes: 17 additions & 7 deletions Justfile
Original file line number Diff line number Diff line change
Expand Up @@ -26,26 +26,23 @@ update:
{{PIP}} install -U \
build \
pytest \
docutils \
pytest-sugar \
pytest-clarity \
freezegun \
responses \
coverage \
tox \
ipython \
flake8 \
black \
twine \
bump \
ruff \
isort

# Create a virtual environment if needed
venv:
#!/usr/bin/env bash
if [ ! -d {{VENV}} ]; then
echo Creating virtual env in dir {{VENV}} ...
python3 -m venv {{VENV}}
python3 -m venv --prompt when {{VENV}}
fi
# Create virtual environment and install / update all dev dependencies
Expand Down Expand Up @@ -103,13 +100,22 @@ purge: clean clean-dev rmvenv clean-build
build:
{{BIN}}/python -m build --outdir ./.dev/dist

# Run linter and code formatter checks
check:
@echo Linting...
-{{BIN}}/ruff check --diff src/when tests

@echo Format checks...
-{{BIN}}/ruff format --diff --line-length 100 src/when tests

# Run linter and code formatter tools
lint:
@echo Linting...
-{{BIN}}/flake8 src/when tests
-{{BIN}}/ruff check src/when tests

@echo Format checks...
-{{BIN}}/black --check --diff -l 100 src/when tests
-{{BIN}}/ruff format --line-length 100 src/when tests


# Launch sqlite data browser (macOS only)
[macos]
Expand All @@ -133,6 +139,10 @@ strftime:
"Notes:\n* - Locale-dependent\n+ - C99 extension\n! - when extension"
)
# Run a command from the venv bin directory
run *args:
{{BIN}}/"$@"

# Execute the when command with any arbitrary arguments
when *args:
@{{BIN}}/when "$@"
Expand Down
32 changes: 27 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,34 @@
[![PyPI](https://img.shields.io/pypi/v/when.svg)](https://pypi.python.org/pypi/when)

**Scenario:** Your favorite sporting event, concert, performance, conference, or symposium is happening
in Ulaanbaatar, Mongolia and all you know is the time of the event relative to the location.
in Ulan Bator, Mongolia and all you have is the time of the event relative to the location -- Feb 8, 3pm.

* What time is it currently in Ulan Bator?

```console
$ when --source "Ulan Bator"
2025-02-07 03:08:58+0800 (+08, Asia/Ulaanbaatar) 038d05w (Ulan Bator, Ulaanbaatar, MN, Asia/Ulaanbaatar)[🌓 First Quarter]
```
* What time is the event in your local time (PST for me, currently)?

```console
$ when --source "Ulan Bator" Feb 8 3pm
2025-02-07 23:00:00-0800 (PST, America/Los_Angeles) 038d05w [🌓 First Quarter]
```

* So what time is that for you in your local time?
* What time did it or will it occur at some other time, past or present?

```console
$ when --source "Ulan Bator" --offset +15d6h
2025-02-22 09:18:01+0800 (+08, Asia/Ulaanbaatar) 053d07w (Ulan Bator, Ulaanbaatar, MN, Asia/Ulaanbaatar)[🌗 Last Quarter]
```
* What about for your friends in other locations around the world?

```console
$ when --exact --target London,GB --source "Ulan Bator" Feb 8 3pm
2025-02-08 07:00:00+0000 (GMT, Europe/London) 039d05w (London, England, GB, Europe/London)[🌓 First Quarter]
```

## Table of Contents

- [Features](#features)
Expand Down Expand Up @@ -548,7 +570,7 @@ $ when --config

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

To begin, lets create the file:
To begin, create the file:

```console
$ cat << EOF > .whenrc.toml
Expand All @@ -561,13 +583,13 @@ grouped = " ➡️ From "
EOF
```

Now, verify your configuration by doing:
Now, verify the configuration by doing:

```console
$ when --config
```

At the top of the output, you should see a line similar to the following:
At the top of the output there should be a line similar to the following:

```console
# Read from /path/to/pwd/.whenrc.toml
Expand Down
12 changes: 9 additions & 3 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -22,9 +22,10 @@ classifiers = [
"Operating System :: MacOS :: MacOS X",
"Programming Language :: Python",
"Programming Language :: Python :: 3",
"Programming Language :: Python :: 3.8",
"Programming Language :: Python :: 3.9",
"Programming Language :: Python :: 3.10",
"Programming Language :: Python :: 3.11",
"Programming Language :: Python :: 3.12",
"Programming Language :: Python :: 3.13",
"Topic :: Software Development",
"Topic :: Software Development :: Libraries",
"Topic :: Software Development :: Libraries :: Python Modules",
Expand All @@ -51,8 +52,13 @@ packages = ["when", "when.db"]
[tool.setuptools.dynamic]
version = { attr = "when.__version__"}

[tool.black]
[tool.ruff]
cache-dir = ".dev/ruff"
line-length = 100
indent-width = 4

[tool.ruff.lint]
ignore = ["E741"]

[tool.pytest.ini_options]
cache_dir = "./.dev/pytest_cache"
Expand Down
3 changes: 1 addition & 2 deletions src/when/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,7 @@
def __getattr__(name):
if name == "when":
from .core import When
from .config import Settings

return When(Settings())
return When()

raise AttributeError(f"What is {name}?")
5 changes: 1 addition & 4 deletions src/when/cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -176,10 +176,7 @@ def get_parser(settings):
)

parser.add_argument(
"--alias",
type=int,
dest="db_alias",
help="Create a new alias from the city id"
"--alias", type=int, dest="db_alias", help="Create a new alias from the city id"
)

parser.add_argument(
Expand Down
55 changes: 27 additions & 28 deletions src/when/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -222,31 +222,6 @@
"""


def overlay(first, other):
if not isinstance(other, dict):
return first

keys = set([*first.keys(), *other.keys()])
result = {}
for key in keys:
if key not in other:
result[key] = first[key]
continue

if key not in first:
result[key] = other[key]
continue

if isinstance(first[key], dict):
result[key] = overlay(first[key], other[key])
continue

result[key] = other[key]

return result



class Settings:
NAME = ".whenrc.toml"
DIRS = [Path.cwd(), Path.home()]
Expand All @@ -269,14 +244,14 @@ def read_path(self, path):
except FileNotFoundError:
pass
else:
self.read_from.append(path)
try:
result = overlay(self.data, data)
result = Settings.overlay(self.data, data)
except Exception as why:
print(f"Unable to settings file {path}, skipping:", file=sys.stderr)
print(f"Unable to import settings file {path}, skipping:", file=sys.stderr)
print(f"{why}", file=sys.stderr)
else:
self.data = result
self.read_from.append(path)

@property
def paths(self):
Expand All @@ -289,3 +264,27 @@ def write_text(self):

out = toml.dumps(self.data)
return f"{text}{out}"

@staticmethod
def overlay(first, other):
if not isinstance(other, dict):
return first

keys = set([*first.keys(), *other.keys()])
result = {}
for key in keys:
if key not in other:
result[key] = first[key]
continue

if key not in first:
result[key] = other[key]
continue

if isinstance(first[key], dict):
result[key] = Settings.overlay(first[key], other[key])
continue

result[key] = other[key]

return result
16 changes: 7 additions & 9 deletions src/when/core.py
Original file line number Diff line number Diff line change
@@ -1,15 +1,13 @@
import fnmatch
import json
import logging
import re
from datetime import date, datetime, timedelta
from itertools import chain

from dateutil import rrule
from dateutil.easter import easter

from . import exceptions, timezones, utils
from .config import DEFAULT_FORMAT, FORMAT_SPECIFIERS
from . import exceptions, timezones, utils, config
from .db import client
from .lunar import lunar_phase

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

self.c99_specs = [fs[0][1] for fs in FORMAT_SPECIFIERS if "+" in fs[-1]]
self.when_specs = [fs[0][2] for fs in FORMAT_SPECIFIERS if "!" == fs[-1]]
self.cond_specs = [fs[0][2] for fs in FORMAT_SPECIFIERS if "!!" == fs[-1]]
self.c99_specs = [fs[0][1] for fs in config.FORMAT_SPECIFIERS if "+" in fs[-1]]
self.when_specs = [fs[0][2] for fs in config.FORMAT_SPECIFIERS if "!" == fs[-1]]
self.cond_specs = [fs[0][2] for fs in config.FORMAT_SPECIFIERS if "!!" == fs[-1]]
self.delta = delta

def token_replacement(self, result, value, pattern, specs, prefix):
Expand Down Expand Up @@ -149,7 +147,7 @@ def c99_F(self, result):

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

def c99_G(self, result):
"Week-based year: 2001"
Expand Down Expand Up @@ -255,8 +253,8 @@ def __repr__(self):


class When:
def __init__(self, settings, local_zone=None, db=None):
self.settings = settings
def __init__(self, settings=None, local_zone=None, db=None):
self.settings = settings or config.Settings()
self.db = db or client.DB()
self.tz_dict = {z: z for z in utils.all_zones()}
for key in list(self.tz_dict):
Expand Down
1 change: 0 additions & 1 deletion src/when/db/__init__.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import sys

from . import make, client
from .. import utils

CITY_FILE_SIZES = make.CITY_FILE_SIZES

Expand Down
5 changes: 2 additions & 3 deletions src/when/db/client.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,9 @@
import os
import re
import contextlib
import logging
import sqlite3
from collections import namedtuple
import contextlib
from pathlib import Path
from collections import namedtuple

from .. import utils
from ..exceptions import DBError
Expand Down
24 changes: 2 additions & 22 deletions src/when/db/make.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import io
import time
import logging
import zipfile
from collections import defaultdict, namedtuple
from pathlib import Path
Expand All @@ -9,26 +8,7 @@

logger = utils.logger()
GeoCity = namedtuple(
"GeoCity",
"gid,"
"name,"
"aname,"
"alt,"
"lat,"
"lng,"
"fclass,"
"fcode,"
"co,"
"cc2,"
"a1,"
"a2,"
"a3,"
"a4,"
"pop,"
"el,"
"dem,"
"tz,"
"mod"
"GeoCity", "gid,name,aname,alt,lat,lng,fclass,fcode,co,cc2,a1,a2,a3,a4,pop,el,dem,tz,mod"
)

DB_DIR = Path(__file__).parent
Expand All @@ -55,7 +35,7 @@ def fetch_cities(size, dirname=DB_DIR):
start = time.time()
zip_bytes = utils.fetch(url)
end = time.time()
logger.info(f"Received {len(zip_bytes):,} bytes in {end-start:,}s")
logger.info(f"Received {len(zip_bytes):,} bytes in {end - start:,}s")
zip_filename = io.BytesIO(zip_bytes)
with zipfile.ZipFile(zip_filename) as z:
z.extract(txt_filename.name, txt_filename.parent)
Expand Down
1 change: 0 additions & 1 deletion src/when/timezones.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import re
import sys

from dateutil.tz import gettz, tzoffset

Expand Down
Loading

0 comments on commit 6a248de

Please sign in to comment.