Skip to content

Commit

Permalink
uv and pre-commit (#29)
Browse files Browse the repository at this point in the history
  • Loading branch information
pmaher86 authored Oct 4, 2024
1 parent cdadb42 commit f2eaff8
Show file tree
Hide file tree
Showing 15 changed files with 95 additions and 84 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/build-and-deploy.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -108,4 +108,4 @@ jobs:
- name: Publish distribution 📦 to TestPyPI
uses: pypa/gh-action-pypi-publish@release/v1
with:
repository-url: https://test.pypi.org/legacy/
repository-url: https://test.pypi.org/legacy/
48 changes: 25 additions & 23 deletions .github/workflows/build-and-test.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -4,32 +4,34 @@ on: [push]

jobs:
build:

runs-on: ubuntu-latest
strategy:
matrix:
python-version: ["3.9", "3.10", "3.11"]

steps:
- uses: actions/checkout@v4
- name: Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@v4
with:
python-version: ${{ matrix.python-version }}
cache: pip

- name: Install dependencies
run: |
pip install --upgrade pip
pip install -r requirements.txt
- name: Install wkhtmltopdf
run: |
sudo apt-get update
sudo apt-get -y install wkhtmltopdf
- name: Install package
run: pip install .[dev]

- name: Test with pytest
run: pytest -vv
- uses: actions/checkout@v4
- name: Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@v4
with:
python-version: ${{ matrix.python-version }}
cache: pip

- name: Install dependencies
run: |
pip install --upgrade pip
pip install -r requirements.txt
- name: Install wkhtmltopdf
run: |
sudo apt-get update
sudo apt-get -y install wkhtmltopdf
- name: Install package
run: pip install .[dev]

- name: Run pre-commit
run: pre-commit run --show-diff-on-failure --color=always --all-files

- name: Test with pytest
run: pytest -vv
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -10,3 +10,4 @@ blacksquare.egg-info/
docs/_build/
docs/_static/
docs/_templates/
dev/
14 changes: 14 additions & 0 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
repos:
- repo: https://github.com/pre-commit/pre-commit-hooks
rev: v3.2.0
hooks:
- id: trailing-whitespace
- id: end-of-file-fixer
- id: check-yaml
- id: check-added-large-files
- repo: https://github.com/astral-sh/ruff-pre-commit
rev: v0.6.8
hooks:
- id: ruff
args: [--fix]
- id: ruff-format
2 changes: 1 addition & 1 deletion .readthedocs.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -19,4 +19,4 @@ sphinx:
# Optionally declare the Python requirements required to build your docs
python:
install:
- requirements: requirements.txt
- requirements: requirements.txt
8 changes: 4 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -110,7 +110,7 @@ ScoredWord(word='MACDUFF', score=25.387070408819042)
Custom word lists are supported and can be passed into the `Crossword` constructor or any of the solving methods. The default word list used is the [Crossword Nexus Collaborative Word List](https://github.com/Crossword-Nexus/collaborative-word-list).

## Example: full symmetry puzzles
As an example of how blacksquare's abstractions allow for non-trivial crossword construction, consider the [June 6 2023 NYT puzzle](https://www.xwordinfo.com/Crossword?date=6/6/2023), which displays not only a rotationaly symmetric grid but a rotationally symmetric *fill*. While this might seem daunting to build, all we have to do is override the `set_word` method of `Crossword` to fill two words at once, and then restrict our wordlist to emordnilaps (words that are also a word when reversed).
As an example of how blacksquare's abstractions allow for non-trivial crossword construction, consider the [June 6 2023 NYT puzzle](https://www.xwordinfo.com/Crossword?date=6/6/2023), which displays not only a rotationaly symmetric grid but a rotationally symmetric *fill*. While this might seem daunting to build, all we have to do is override the `set_word` method of `Crossword` to fill two words at once, and then restrict our wordlist to emordnilaps (words that are also a word when reversed).
```python
class SymmetricCrossword(Crossword):
def set_word(self, word_index: WordIndex, value: str) -> None:
Expand All @@ -129,7 +129,7 @@ xw = SymmetricCrossword(15)
filled = [
(0, 3), (0, 4), (0, 5), (0, 11), (1, 4), (1, 5), (1, 11),
(2, 4), (2, 11), (3, 4), (3, 9), (4, 0), (4, 1), (4, 2),
(4, 7), (4, 8), (4, 14), (5, 6), (5, 12), (5, 13), (5, 14),
(4, 7), (4, 8), (4, 14), (5, 6), (5, 12), (5, 13), (5, 14),
(6, 5), (6, 10), (7, 3),
]
for i in filled:
Expand Down Expand Up @@ -176,9 +176,9 @@ There's clearly some extra curation that could be done to improve the word list,
You'll also need to install [wkhtmltopdf](https://wkhtmltopdf.org/) for .pdf export to work.

## Future plans
Blacksquare is not a GUI application and isn't intended to be one. Blacksquare is also not a package for solving crossword puzzles.
Blacksquare is not a GUI application and isn't intended to be one. Blacksquare is also not a package for solving crossword puzzles.

Possible extensions include:
Possible extensions include:
- [ ] Other file formats
- [ ] Other/better filling heuristics
- [ ] Verifying puzzle validity
Expand Down
15 changes: 4 additions & 11 deletions pyproject.toml
Original file line number Diff line number Diff line change
@@ -1,14 +1,10 @@
[build-system]
requires = [
"setuptools>=42", "wheel", "setuptools-scm"
]
requires = ["setuptools>=42", "wheel", "setuptools-scm"]

[project]
name = "blacksquare"
description = "A package for creating crossword puzzles"
authors = [
{ name = "Patrick Maher", email = "pmaher86@gmail.com" }
]
authors = [{ name = "Patrick Maher", email = "pmaher86@gmail.com" }]
readme = "README.md"
dependencies = [
"networkx",
Expand All @@ -31,10 +27,7 @@ dynamic = ["version"]
Source = "https://github.com/pmaher86/blacksquare"

[project.optional-dependencies]
dev = [
"pip-tools",
"pytest",
]
dev = ["pre-commit", "pytest", "uv"]

[tool.setuptools_scm]
local_scheme = "no-local-version"
Expand All @@ -43,4 +36,4 @@ local_scheme = "no-local-version"
where = ["src"]

[tool.setuptools.package-data]
blacksquare = ["*.dict"]
blacksquare = ["*.dict"]
54 changes: 23 additions & 31 deletions requirements.txt
Original file line number Diff line number Diff line change
@@ -1,15 +1,13 @@
#
# This file is autogenerated by pip-compile with Python 3.10
# by the following command:
#
# pip-compile --extra=dev pyproject.toml
#
build==1.0.3
# via pip-tools
click==8.1.7
# via pip-tools
exceptiongroup==1.1.3
# via pytest
# This file was autogenerated by uv via the following command:
# uv pip compile --extra=dev pyproject.toml -o requirements.txt
cfgv==3.4.0
# via pre-commit
distlib==0.3.8
# via virtualenv
filelock==3.16.1
# via virtualenv
identify==2.6.1
# via pre-commit
iniconfig==2.0.0
# via pytest
markdown-it-py==3.0.0
Expand All @@ -18,51 +16,45 @@ mdurl==0.1.2
# via markdown-it-py
networkx==3.1
# via blacksquare (pyproject.toml)
nodeenv==1.9.1
# via pre-commit
numpy==1.26.0
# via
# blacksquare (pyproject.toml)
# pandas
packaging==23.1
# via
# build
# pytest
# via pytest
pandas==2.1.1
# via blacksquare (pyproject.toml)
pdfkit==1.0.0
# via blacksquare (pyproject.toml)
pip-tools==7.3.0
# via blacksquare (pyproject.toml)
platformdirs==4.3.6
# via virtualenv
pluggy==1.3.0
# via pytest
pre-commit==3.8.0
# via blacksquare (pyproject.toml)
puzpy==0.2.5
# via blacksquare (pyproject.toml)
pygments==2.16.1
# via rich
pypdf==3.16.2
# via blacksquare (pyproject.toml)
pyproject-hooks==1.0.0
# via build
pytest==7.4.2
# via blacksquare (pyproject.toml)
python-dateutil==2.8.2
# via pandas
pytz==2023.3.post1
# via pandas
pyyaml==6.0.2
# via pre-commit
rich==13.5.3
# via blacksquare (pyproject.toml)
six==1.16.0
# via python-dateutil
tomli==2.0.1
# via
# build
# pip-tools
# pyproject-hooks
# pytest
tzdata==2023.3
# via pandas
wheel==0.41.2
# via pip-tools

# The following packages are considered to be unsafe in a requirements file:
# pip
# setuptools
uv==0.4.18
# via blacksquare (pyproject.toml)
virtualenv==20.26.6
# via pre-commit
11 changes: 11 additions & 0 deletions src/blacksquare/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,3 +5,14 @@

BLACK, EMPTY = SpecialCellValue.BLACK, SpecialCellValue.EMPTY
ACROSS, DOWN = Direction.ACROSS, Direction.DOWN

__all__ = [
"Crossword",
"Symmetry",
"DEFAULT_WORDLIST",
"WordList",
"BLACK",
"EMPTY",
"ACROSS",
"DOWN",
]
2 changes: 1 addition & 1 deletion src/blacksquare/crossword.py
Original file line number Diff line number Diff line change
Expand Up @@ -211,7 +211,7 @@ def to_pdf(
<div style='font-size:18pt;'>
{header_html}
</div>
<div style='position:absolute;left:50%;top:50%;transform: translate(-50%, -50%);'>
<div style='position:absolute;left:50%;top:50%;transform: translate(-50%, -50%);'>
{self._grid_html(size_px=600)}
</div>
</body></html>
Expand Down
4 changes: 1 addition & 3 deletions src/blacksquare/symmetry.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,9 @@
import enum
from dataclasses import dataclass
from typing import List, Optional, Tuple, Union
from typing import List, Union

import numpy as np

from blacksquare.types import CellIndex


@dataclass
class SymmetryResult:
Expand Down
1 change: 1 addition & 0 deletions src/blacksquare/types.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ def __repr__(self):

class SpecialCellValue(Enum):
"An enum representing blank and empty cell values in a crossword."

BLACK = "Black"
EMPTY = "Empty"

Expand Down
6 changes: 3 additions & 3 deletions src/blacksquare/word.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,7 @@
import numpy as np

from blacksquare.types import Direction, SpecialCellValue, WordIndex
from blacksquare.word_list import (INVERSE_CHARACTER_FREQUENCIES,
MatchWordList, WordList)
from blacksquare.word_list import INVERSE_CHARACTER_FREQUENCIES, MatchWordList, WordList

if TYPE_CHECKING:
from blacksquare.cell import Cell
Expand Down Expand Up @@ -137,7 +136,8 @@ def score_word_fn(word: str, score: float) -> float:
per_letter_scores = [
letter_scores_per_index[i].get(word[i], 0)
* INVERSE_CHARACTER_FREQUENCIES.get(word[i], 1)
for i in open_indices if i in letter_scores_per_index
for i in open_indices
if i in letter_scores_per_index
]
return np.log(np.prod(per_letter_scores) + 1) * score

Expand Down
8 changes: 4 additions & 4 deletions src/blacksquare/word_list.py
Original file line number Diff line number Diff line change
Expand Up @@ -173,7 +173,7 @@ def find_matches_str(self, query: str) -> MatchWordList:
@cached_property
def words(self) -> list[str]:
return list(self._words)

@cached_property
def _words_dict(self) -> dict[str, float]:
return dict(zip(self._words, self._scores))
Expand All @@ -192,7 +192,6 @@ def get_score(self, word: str) -> Optional[float]:
Optional[float]: The score. None if word is not in word list.
"""
return self._words_dict.get(word)


@cached_property
def frame(self) -> pd.DataFrame:
Expand All @@ -209,7 +208,7 @@ def score_filter(self, threshold: float) -> WordList:
"""
score_mask = self._scores >= threshold
return WordList(dict(zip(self._words[score_mask], self._scores[score_mask])))

def filter(self, filter_fn: Callable[[ScoredWord], bool]) -> WordList:
return WordList(dict([w for w in self if filter_fn(w)]))

Expand Down Expand Up @@ -245,6 +244,7 @@ def __add__(self, other):
def __contains__(self, item):
return item in self._words_set


class MatchWordList(WordList):
"""An object representing a WordList matching an open word. This class should not be
constructed by the user, it is generated by matching methods on the original word
Expand Down Expand Up @@ -332,4 +332,4 @@ def _normalize(word: str) -> str:


# DEFAULT_WORDLIST = WordList(pkg_resources.resource_stream(__name__, "xwordlist.dict"))
DEFAULT_WORDLIST = WordList(files("blacksquare").joinpath("xwordlist.dict"))
DEFAULT_WORDLIST = WordList(files("blacksquare").joinpath("xwordlist.dict"))
3 changes: 1 addition & 2 deletions tests/test_crossword.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
from pathlib import Path

import numpy as np
import pytest

from blacksquare import ACROSS, BLACK, DOWN, EMPTY, Crossword, WordList
Expand Down Expand Up @@ -104,7 +103,7 @@ def test_to_puz(self, xw, tmp_path):
xw.to_puz(filename)
assert tmp_path.exists()
loaded = Crossword.from_puz(filename)
assert all(l == x for l, x in zip(loaded.itercells(), xw.itercells()))
assert all(x == y for x, y in zip(loaded.itercells(), xw.itercells()))

def test_from_puz(self):
Crossword.from_puz(Path(__file__).parent / "dummy.puz")
Expand Down

0 comments on commit f2eaff8

Please sign in to comment.