Solutions to Advent of Code 2020
I'm using this project to play with Python, Testing and Github Actions
pytest is a testing library for Python. It is widely used and has some advantages compared to the built-in unitest (here's a comparison between pytest and unittest).
It has a powerfull test discovery, detecting test by default in functions beginning with test and in Python files beginning with test_ or ending with _test.
pytest recommends putting tests in an extra directory outside the application code.
src/
__init__.py
day_01/
part_1.py
part_2.py
day_02/
part_1.py
tests/
__init__.py
day_01/
__init__.py
test_part_1.py
test_part_2.py
day_02/
__init__.py
test_part_1.py
When executing pytest from root folder, it will detect the following files starting with test_
- src/tests/day_01/test_part_1.py
- src/tests/day_01/test_part_2.py
- src/tests/day_02/test_part_1.py
Thanks to __init__py in both day_01 and day_02 folders we can use test files with the same name, as __init__py declares a directory as a Python package.
To be able to import local code there is an __init__.py also in tests and in src.
Now test_part_1.py can load code from all folders under src, using . instead of /
from src.day_01.part_1 import solutionTo gain efficiency it is possible to run pytest on code change and only on impacted tests. pytest has two plugins that combined together provide this feature, pytest-testmon and pytest-watch.
pytest-watch is a CLI tool that runs pytest, and re-run it when a file changes.
pytest-testmon uses coverage.py to determine which tests are impacted by changes in code.
Install these plugin using pip
pip install pytest-watch pytest-testmonAnd run pytest-watcher using pytest --testmon
ptw --runner "pytest --testmon"Github built-in CI/CD is free for public repositories since Aug, 2019. It has many workflow templates, including one for Python applications. To add it and start running linting and tests on Github, click on Actions -> New Workflow -> Python Applications. This will create a new configuration yaml under .github/workflows, that by defaults execute the actions at every push on main branch
Flake8 is a widely used Python linter. It supports storing its configuration in the root directory in a .flake8 file, within a [flake8] section.
Black is a widely used Python formatter. It supports storing its configuration in a TOML file within a [tool.black] section. It can be used to format code on save, or to format all files from the command line:
black .Here's a collection of resources and learnings from 2020 edition
- Parsing mathematical expressions with Dijkstra's Shunting-yard algorithm - from day 18
- Hexagonal grids - from day 24
re.match looks for the pattern at the beginning of the string, or at a specific position pos
import re
a_string = 'something whatever'
pattern_at_the_beginning = 'some'
pattern_in_the_middle = 'g what'
print re.match(pattern_at_the_beginning, a_string) # matches
print re.match(pattern_in_the_middle, a_string) # doesn't match
# using a specific position where to look for the pattern
print re.match(pattern_in_the_middle, a_string, pos=8) # matchesre.search scans through the string looking for a position where the pattern is matched.
import re
a_string = 'something whatever'
pattern_at_the_beginning = 'some'
pattern_in_the_middle = 'g what'
print re.search(pattern_at_the_beginning, a_string) # matches
print re.search(pattern_in_the_middle, a_string) # matchesre.fullmatch returns a match object iff the whole string matches the regular expression pattern
import re
a_string = 'something whatever'
pattern_at_the_beginning = 'some'
pattern_in_the_middle = 'g what'
print re.fullmatch(pattern_at_the_beginning, a_string) # doesn't match
print re.fullmatch(pattern_in_the_middle, a_string) # doesn't match
pattern_full = '\w+ \w+' # 1 or more word characters + space + 1 or more word characters
print re.fullmatch(pattern_full, a_string) # matchesMore on match vs search in Python's official documentation
