Skip to content

Commit d867bd8

Browse files
authored
refactor (#12)
- factor out runner for aocr to use - more logging - change tmpdir to tmp_path - update CI matrix
1 parent a385645 commit d867bd8

15 files changed

+197
-92
lines changed

.travis.yml

Lines changed: 3 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -4,21 +4,13 @@ sudo: false
44

55
python:
66
- "2.7"
7-
- "3.5"
87
- "3.6"
8+
- "3.7"
9+
- "3.8"
910
- "pypy"
10-
- "pypy3.5"
11+
- "pypy3"
1112
- "nightly"
1213

13-
matrix:
14-
fast_finish: true
15-
allow_failures:
16-
- python: "nightly"
17-
include:
18-
- python: 3.7
19-
dist: xenial
20-
sudo: true
21-
2214
install:
2315
- pip install --upgrade pytest setuptools
2416
- pip install --editable .

aocd/__init__.py

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,16 +7,22 @@
77
import sys
88
from functools import partial
99

10-
from .get import get_data
11-
from .get import get_day_and_year
10+
from . import cli
11+
from . import exceptions
12+
from . import get
13+
from . import models
14+
from . import post
15+
from . import runner
16+
from . import utils
17+
from . import version
1218
from .exceptions import AocdError
1319
from .exceptions import PuzzleUnsolvedError
20+
from .get import get_data
21+
from .get import get_day_and_year
1422
from .post import submit
1523
from .utils import AOC_TZ
1624
from .version import __version__
1725

18-
from . import cli, exceptions, get, models, post, runner, utils, version
19-
2026

2127
__all__ = [
2228
"cli",

aocd/models.py

Lines changed: 14 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,8 @@ def default_user():
6363
You can find it in your browser cookies after login.
6464
1) Save the cookie into a text file {}, or
6565
2) Export the cookie in environment variable AOC_SESSION
66+
67+
See https://github.com/wimglenn/advent-of-code-wim/issues/1 for more info.
6668
"""
6769
)
6870
cprint(msg.format(AOCD_DIR + "/token"), color="red", file=sys.stderr)
@@ -98,6 +100,10 @@ def user(self):
98100

99101
@property
100102
def input_data(self):
103+
if self.user.token == ".aocr":
104+
with open("input.txt") as f:
105+
return f.read()
106+
sanitized = "..." + self.user.token[-4:]
101107
try:
102108
# use previously received data, if any existing
103109
with io.open(self.input_data_fname, encoding="utf-8") as f:
@@ -106,22 +112,21 @@ def input_data(self):
106112
if err.errno != errno.ENOENT:
107113
raise
108114
else:
109-
sanitized = self.user.token[:4] + "..." + self.user.token[-4:]
110115
sanitized_path = self.input_data_fname.replace(self.user.token, sanitized)
111-
log.info("reusing existing data %s", sanitized_path)
116+
log.debug("reusing existing data %s", sanitized_path)
112117
return data.rstrip("\r\n")
113-
log.info("getting data year=%s day=%s", self.year, self.day)
118+
log.info("getting data year=%s day=%s token=%s", self.year, self.day, sanitized)
114119
response = requests.get(
115120
url=self.input_data_url, cookies=self._cookies, headers=self._headers
116121
)
117122
if not response.ok:
118-
log.error("got %s status code", response.status_code)
123+
log.error("got %s status code token=%s", response.status_code, sanitized)
119124
log.error(response.text)
120125
raise AocdError("Unexpected response")
121126
data = response.text
122127
_ensure_intermediate_dirs(self.input_data_fname)
123128
with open(self.input_data_fname, "w") as f:
124-
log.info("saving the puzzle input")
129+
log.info("saving the puzzle input token=%s", sanitized)
125130
f.write(data)
126131
return data.rstrip("\r\n")
127132

@@ -200,7 +205,8 @@ def _submit(self, value, part, reopen=True, quiet=False):
200205
cprint(bad_guesses[str(value)], "red")
201206
return
202207
url = self.submit_url
203-
log.info("posting %r to %s (part %s)", value, url, part)
208+
sanitized = "..." + self.user.token[-4:]
209+
log.info("posting %r to %s (part %s) token=%s", value, url, part, sanitized)
204210
level = {"a": 1, "b": 2}[part]
205211
response = requests.post(
206212
url=url,
@@ -285,6 +291,8 @@ def _get_answer(self, part):
285291
Note: Answers are only revealed after a correct submission. If you've
286292
have not already solved the puzzle, AocdError will be raised.
287293
"""
294+
if part == "b" and self.day == 25:
295+
return None
288296
answer_fname = getattr(self, "answer_{}_fname".format(part))
289297
if os.path.isfile(answer_fname):
290298
with open(answer_fname) as f:

aocd/post.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,9 @@
66

77
import logging
88

9+
from .exceptions import AocdError
910
from .get import current_day
1011
from .get import most_recent_year
11-
from .exceptions import AocdError
1212
from .models import default_user
1313
from .models import Puzzle
1414
from .models import User

aocd/runner.py

Lines changed: 50 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -8,14 +8,15 @@
88
import json
99
import logging
1010
import os
11-
import pkg_resources
1211
import sys
12+
import tempfile
1313
import time
1414
from argparse import ArgumentParser
1515
from collections import OrderedDict
1616
from datetime import datetime
1717

1818
import pebble
19+
import pkg_resources
1920
from termcolor import colored
2021

2122
from .models import default_user
@@ -81,17 +82,18 @@ def run_with_timeout(entry_point, timeout, progress, dt=0.1, **kwargs):
8182
# TODO : multi-process over the different tokens
8283
spinner = itertools.cycle(r"\|/-")
8384
pool = pebble.ProcessPool(max_workers=1)
84-
line = walltime = format_time(0)
85+
line = elapsed = format_time(0)
8586
with pool:
8687
t0 = time.time()
8788
func = entry_point.load()
8889
future = pool.schedule(func, kwargs=kwargs, timeout=timeout)
8990
while not future.done():
90-
line = "\r" + walltime + " " + progress + " " + next(spinner)
91-
sys.stderr.write(line)
92-
sys.stderr.flush()
91+
if progress is not None:
92+
line = "\r" + elapsed + " " + progress + " " + next(spinner)
93+
sys.stderr.write(line)
94+
sys.stderr.flush()
9395
time.sleep(dt)
94-
walltime = format_time(time.time() - t0)
96+
elapsed = format_time(time.time() - t0, timeout)
9597
walltime = time.time() - t0
9698
try:
9799
a, b = future.result()
@@ -100,8 +102,12 @@ def run_with_timeout(entry_point, timeout, progress, dt=0.1, **kwargs):
100102
crashed = True
101103
else:
102104
crashed = False
103-
sys.stderr.write("\r" + " " * len(line) + "\r")
104-
sys.stderr.flush()
105+
# longest correct answer seen so far has been 32 chars
106+
a = str(a)[:50]
107+
b = str(b)[:50]
108+
if progress is not None:
109+
sys.stderr.write("\r" + " " * len(line) + "\r")
110+
sys.stderr.flush()
105111
return a, b, walltime, crashed
106112

107113

@@ -116,6 +122,29 @@ def format_time(t, timeout=DEFAULT_TIMEOUT):
116122
return runtime
117123

118124

125+
def run_one(year, day, input_data, entry_point, timeout=DEFAULT_TIMEOUT, progress=None):
126+
prev = os.getcwd()
127+
scratch = tempfile.mkdtemp(prefix="{}-{:02d}-".format(year, day))
128+
os.chdir(scratch)
129+
assert not os.path.exists("input.txt")
130+
try:
131+
with open("input.txt", "w") as f:
132+
f.write(input_data)
133+
a, b, walltime, crashed = run_with_timeout(
134+
entry_point=entry_point,
135+
timeout=timeout,
136+
year=year,
137+
day=day,
138+
data=input_data,
139+
progress=progress,
140+
)
141+
finally:
142+
os.unlink("input.txt")
143+
os.chdir(prev)
144+
os.rmdir(scratch)
145+
return a, b, walltime, crashed
146+
147+
119148
def run_for(plugins, years, days, datasets, timeout=DEFAULT_TIMEOUT, autosubmit=True):
120149
aoc_now = datetime.now(tz=AOC_TZ)
121150
all_entry_points = pkg_resources.iter_entry_points(group="adventofcode.user")
@@ -130,17 +159,20 @@ def run_for(plugins, years, days, datasets, timeout=DEFAULT_TIMEOUT, autosubmit=
130159
for year, day, plugin, dataset in it:
131160
if year == aoc_now.year and day > aoc_now.day:
132161
continue
133-
os.environ["AOC_SESSION"] = datasets[dataset]
162+
token = datasets[dataset]
163+
entry_point = entry_points[plugin]
164+
os.environ["AOC_SESSION"] = token
134165
puzzle = Puzzle(year=year, day=day)
135-
progress = "{0.year}/{0.day:<2d} - {0.title:<40} {1:>%d}/{2:<%d}"
166+
title = puzzle.title
167+
progress = "{}/{:<2d} - {:<40} {:>%d}/{:<%d}"
136168
progress %= (userpad, datasetpad)
137-
progress = progress.format(puzzle, plugin, dataset)
138-
a, b, walltime, crashed = run_with_timeout(
139-
entry_point=entry_points[plugin],
169+
progress = progress.format(year, day, title, plugin, dataset)
170+
a, b, walltime, crashed = run_one(
171+
year=year,
172+
day=day,
173+
input_data=puzzle.input_data,
174+
entry_point=entry_point,
140175
timeout=timeout,
141-
year=puzzle.year,
142-
day=puzzle.day,
143-
data=puzzle.input_data,
144176
progress=progress,
145177
)
146178
runtime = format_time(walltime, timeout)
@@ -162,6 +194,8 @@ def run_for(plugins, years, days, datasets, timeout=DEFAULT_TIMEOUT, autosubmit=
162194
except AttributeError:
163195
pass
164196
correct = str(expected) == answer
197+
if crashed:
198+
assert not correct
165199
icon = colored("✔", "green") if correct else colored("✖", "red")
166200
correction = ""
167201
if not correct:

setup.cfg

Lines changed: 0 additions & 5 deletions
This file was deleted.

setup.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,4 +33,5 @@
3333
"pebble",
3434
'colorama; platform_system == "Windows"',
3535
],
36+
options={"bdist_wheel": {"universal": "1"}},
3637
)

tests/conftest.py

Lines changed: 15 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
from __future__ import unicode_literals
2+
13
import pytest
24

35

@@ -7,24 +9,24 @@ def mocked_sleep(mocker):
79
return no_sleep_till_brooklyn
810

911

10-
@pytest.fixture(autouse=True)
11-
def remove_user_env(tmpdir, monkeypatch):
12-
memo_dir = tmpdir / ".config/aocd"
13-
monkeypatch.setattr("aocd.runner.AOCD_DIR", str(memo_dir))
14-
monkeypatch.setattr("aocd.models.AOCD_DIR", str(memo_dir))
15-
monkeypatch.delenv("AOC_SESSION", raising=False)
16-
17-
1812
@pytest.fixture
19-
def aocd_dir(tmpdir):
20-
data_dir = tmpdir / ".config/aocd"
21-
data_dir.ensure_dir()
13+
def aocd_dir(tmp_path):
14+
data_dir = tmp_path / ".config" / "aocd"
15+
data_dir.mkdir(parents=True)
2216
return data_dir
2317

2418

19+
@pytest.fixture(autouse=True)
20+
def remove_user_env(aocd_dir, monkeypatch):
21+
monkeypatch.setattr("aocd.runner.AOCD_DIR", str(aocd_dir))
22+
monkeypatch.setattr("aocd.models.AOCD_DIR", str(aocd_dir))
23+
monkeypatch.delenv("AOC_SESSION", raising=False)
24+
25+
2526
@pytest.fixture(autouse=True)
2627
def test_token(aocd_dir):
2728
token_file = aocd_dir / "token"
28-
token_file.ensure(file=True)
29-
token_file.write("thetesttoken")
29+
token_dir = aocd_dir / "thetesttoken"
30+
token_dir.mkdir()
31+
token_file.write_text("thetesttoken")
3032
return token_file

tests/test_auth.py

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,13 @@
1+
from __future__ import unicode_literals
2+
13
import pytest
24

35
from aocd.exceptions import AocdError
46
from aocd.models import default_user
57

68

79
def test_no_session_id(test_token, capsys):
8-
test_token.remove()
10+
test_token.unlink()
911
with pytest.raises(AocdError("Missing session ID")):
1012
default_user()
1113
out, err = capsys.readouterr()
@@ -20,7 +22,7 @@ def test_get_session_id_from_env(monkeypatch):
2022

2123

2224
def test_get_session_id_from_file(test_token):
23-
test_token.write("tokenfromfile")
25+
test_token.write_text("tokenfromfile")
2426
user = default_user()
2527
assert user.token == "tokenfromfile"
2628

@@ -32,7 +34,7 @@ def test_env_takes_priority_over_file(monkeypatch, test_token):
3234

3335

3436
def test_problem_loading_session_id_is_left_unhandled(test_token):
35-
test_token.remove()
36-
test_token.ensure_dir()
37+
test_token.unlink()
38+
test_token.mkdir()
3739
with pytest.raises(IOError):
3840
default_user()

tests/test_date_finding.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,9 @@
22

33
import pytest
44

5+
from aocd.exceptions import AocdError
56
from aocd.get import current_day
67
from aocd.get import most_recent_year
7-
from aocd.exceptions import AocdError
88

99

1010
@pytest.mark.parametrize(

0 commit comments

Comments
 (0)