8000 Use pyright in strict mode · mjpieters/adventofcode@30417ff · GitHub
[go: up one dir, main page]

Skip to content

Commit 30417ff

Browse files
committed
Use pyright in strict mode
This requires a git checkout of advent-of-code-data (with a fully typed public API) and some refactoring of day 5 to introduce a generic base.
1 parent f9bf213 commit 30417ff

File tree

4 files changed

+85
-75
lines changed

4 files changed

+85
-75
lines changed

2023/Day 05.ipynb

Lines changed: 40 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,27 @@
2727
"from operator import itemgetter\n",
2828
"\n",
2929
"\n",
30+
"class BaseAlmanac[T: \"AlmanacMap\"]:\n",
31+
" map_cls: type[T]\n",
32+
" maps: dict[str, T]\n",
33+
"\n",
34+
" @classmethod\n",
35+
" def from_entries(cls, *entries: str) -> t.Self:\n",
36+
" seeds_line, *table = entries\n",
37+
" seeds = [int(seed) for seed in seeds_line.partition(\": \")[-1].split()]\n",
38+
" maps = {map_.from_: map_ for map_ in map(cls.map_cls.from_entry, table)}\n",
39+
" return cls(seeds, maps)\n",
40+
"\n",
41+
" def seed_location(self, seed: int) -> int:\n",
42+
" current = \"seed\"\n",
43+
" value = seed\n",
44+
" while current != \"location\":\n",
45+
" map_ = self.maps[current]\n",
46+
" current = map_.to_\n",
47+
" value, _ = map_.lookup(value)\n",
48+
" return value\n",
49+
"\n",
50+
"\n",
3051
"@dataclass\n",
3152
"class AlmanacMap:\n",
3253
" from_: str\n",
@@ -43,7 +64,7 @@
4364
" ]\n",
4465
" return cls(from_, to_, sorted(ranges, key=itemgetter(0)))\n",
4566
"\n",
46-
" def __getitem__(self, value: int) -> tuple[int, int | None]:\n",
67+
" def lookup(self, value: int) -> tuple[int, int | None]:\n",
4768
" \"\"\"Map a value through the almanac table\n",
4869
"\n",
4970
" Returns the new value, and the remaining length of the source section it\n",
@@ -61,26 +82,11 @@
6182
"\n",
6283
"\n",
6384
"@dataclass\n",
64-
"class Almanac:\n",
85+
"class Almanac(BaseAlmanac[AlmanacMap]):\n",
86+
" map_cls = AlmanacMap\n",
6587
" seeds: list[int]\n",
6688
" maps: dict[str, AlmanacMap]\n",
6789
"\n",
68-
" @classmethod\n",
69-
" def from_entries(cls, *entries: str) -> t.Self:\n",
70-
" seeds_line, *entries = entries\n",
71-
" seeds = [int(seed) for seed in seeds_line.partition(\": \")[-1].split()]\n",
72-
" maps = {map_.from_: map_ for map_ in map(AlmanacMap.from_entry, entries)}\n",
73-
" return cls(seeds, maps)\n",
74-
"\n",
75-
" def __getitem__(self, seed: int) -> int:\n",
76-
" current = \"seed\"\n",
77-
" value = seed\n",
78-
" while current != \"location\":\n",
79-
" map_ = self.maps[current]\n",
80-
" current = map_.to_\n",
81-
" value, _ = map_[value]\n",
82-
" return value\n",
83-
"\n",
8490
"\n",
8591
"test_almanac_text = \"\"\"\\\n",
8692
"seeds: 79 14 55 13\n",
@@ -118,7 +124,7 @@
118124
"56 93 4\n",
119125
"\"\"\"\n",
120126
"test_almanac = Almanac.from_entries(*test_almanac_text.split(\"\\n\\n\"))\n",
121-
"assert min(test_almanac[seed] for seed in test_almanac.seeds) == 35"
127+
"assert min(map(test_almanac.seed_location, test_almanac.seeds)) == 35"
122128
]
123129
},
124130
{
@@ -137,8 +143,9 @@
137143
"source": [
138144
"import aocd\n",
139145
"\n",
140-
"almanac = Almanac.from_entries(*aocd.get_data(day=5, year=2023).split(\"\\n\\n\"))\n",
141-
"print(\"Part 1:\", min(almanac[seed] for seed in almanac.seeds))"
146+
"almanac_entries = aocd.get_data(day=5, year=2023).split(\"\\n\\n\")\n",
147+
"almanac = Almanac.from_entries(*almanac_entries)\n",
148+
"print(\"Part 1:\", min(map(almanac.seed_location, almanac.seeds)))"
142149
]
143150
},
144151
{
@@ -166,13 +173,13 @@
166173
"\n",
167174
"\n",
168175
"class RangeAlmanacMap(AlmanacMap):\n",
169-
" def __getitem__(self, values: tuple[range, ...]) -> tuple[range, ...]:\n",
170-
" results = []\n",
176+
" def lookup_range(self, values: tuple[range, ...]) -> tuple[range, ...]:\n",
177+
" results: list[range] = []\n",
171178
" queue = deque(values)\n",
172179
" while queue:\n",
173180
" value = queue.popleft()\n",
174181
" size = len(value)\n",
175-
" dst, remainder = super().__getitem__(value.start)\n",
182+
" dst, remainder = self.lookup(value.start)\n",
176183
" if remainder and size > remainder:\n",
177184
" # process the section that doesn't fit\n",
178185
" queue.append(value[remainder:])\n",
@@ -183,23 +190,17 @@
183190
"\n",
184191
"\n",
185192
"@dataclass\n",
186-
"class RangeAlmanac(Almanac):\n",
193+
"class RangeAlmanac(BaseAlmanac[RangeAlmanacMap]):\n",
194+
" map_cls = RangeAlmanacMap\n",
195+
" seeds: list[int]\n",
187196
" maps: dict[str, RangeAlmanacMap]\n",
188197
"\n",
189-
" @classmethod\n",
190-
" def from_entries(cls, *entries: str) -> t.Self:\n",
191-
" inst = super().from_entries(*entries)\n",
192-
" inst.maps = {\n",
193-
" to_: RangeAlmanacMap(**vars(map_)) for to_, map_ in inst.maps.items()\n",
194-
" }\n",
195-
" return inst\n",
196-
"\n",
197-
" def __getitem__(self, values: tuple[range, ...]) -> int:\n",
198+
" def seed_locations(self, values: tuple[range, ...]) -> int:\n",
198199
" current = \"seed\"\n",
199200
" while current != \"location\":\n",
200201
" map_ = self.maps[current]\n",
201202
" current = map_.to_\n",
202-
" values = map_[values]\n",
203+
" values = map_.lookup_range(values)\n",
203204
" return min(v.start for v in values)\n",
204205
"\n",
205206
" @property\n",
@@ -209,7 +210,7 @@
209210
"\n",
210211
"\n",
211212
"test_almanac = RangeAlmanac.from_entries(*test_almanac_text.split(\"\\n\\n\"))\n",
212-
"assert test_almanac[test_almanac.seed_ranges] == 46"
213+
"assert test_almanac.seed_locations(test_almanac.seed_ranges) == 46"
213214
]
214215
},
215216
{
@@ -226,8 +227,8 @@
226227
}
227228
],
228229
"source": [
229-
"almanac = RangeAlmanac.from_entries(*aocd.get_data(day=5, year=2023).split(\"\\n\\n\"))\n",
230-
"print(\"Part 2:\", almanac[almanac.seed_ranges])"
230+
"almanac = RangeAlmanac.from_entries(*almanac_entries)\n",
231+
"print(\"Part 2:\", almanac.seed_locations(almanac.seed_ranges))"
231232
]
232233
}
233234
],
@@ -247,7 +248,7 @@
247248
"name": "python",
248249
"nbconvert_exporter": "python",
249250
"pygments_lexer": "ipython3",
250-
"version": "3.12.0"
251+
"version": "3.12.1"
251252
}
252253
},
253254
"nbformat": 4,

2023/Day 11.ipynb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@
3636
" [[c == \"#\" for c in line] for line in image.splitlines()], dtype=bool\n",
3737
" )\n",
3838
"\n",
39-
" def pairwise_distances_sum(self, expansion=2) -> int:\n",
39+
" def pairwise_distances_sum(self, expansion: int = 2) -> int:\n",
4040
" coords = np.argwhere(self._galaxies)\n",
4141
" # expand coords\n",
4242
" for col in (0, 1):\n",

poetry.lock

Lines changed: 38 additions & 34 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

pyproject.toml

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,14 +17,18 @@ numpy = "*"
1717
pillow = "*"
1818
marshmallow = "*"
1919
matplotlib = "*"
20-
advent-of-code-data = "*"
2120
scipy = "*"
2221
astor = "*"
2322
sympy = "*"
2423
regex = "*"
2524
requests = "*"
2625
easing-functions = "*"
2726

27+
[tool.poetry.dependencies.advent-of-code-data]
28+
# Commit that provides type hints for the library, use until
29+
# new release is made available.
30+
"git"="https://github.com/wimglenn/advent-of-code-data.git"
31+
"rev"="40d1ef7c99bf26bc5cc60cc6e2aaf2e6054ff596"
2832

2933
[tool.poetry.group.dev.dependencies]
3034
ruff = "^0.1.6"
@@ -44,6 +48,7 @@ select = ["B", "E", "F", "W", "B9"]
4448
include = ["."]
4549
exclude = ["2015", "2016"]
4650
ignore = ["jupyter_notebook_config.py"]
51+
strict = ["2023"]
4752
# pyright 1.1.339 "standard" mode, manually applied until pylance includes that version
4853
reportFunctionMemberAccess = "error"
4954
reportIncompatibleMethodOverride = "error"

0 commit comments

Comments
 (0)
0