Skip to content

Commit

Permalink
Merge pull request #41 from front-seat/dave/pyright
Browse files Browse the repository at this point in the history
Adopt pyright, wanted for future work.
  • Loading branch information
davepeck authored May 22, 2024
2 parents 8773c67 + 7e35a40 commit bd44458
Show file tree
Hide file tree
Showing 14 changed files with 226 additions and 38 deletions.
15 changes: 10 additions & 5 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -17,17 +17,22 @@ jobs:
python-version: 3.12
cache: pip
cache-dependency-path: pyproject.toml
- name: Install dependencies
- name: Set up nodejs 20.13
uses: actions/setup-node@v4
with:
node-version: 20.13
cache: npm
cache-dependency-path: package-lock.json
- name: Install python dependencies
run: pip install -r requirements.txt
- name: Install nodejs dependencies
run: npm install
- name: Check formatting
run: ruff format --check server
- name: Lint
run: ruff check server
- name: Check types
run: mypy server
env:
SECRET_KEY: "test"
BASE_URL: "https://example.com"
run: npx pyright
- name: Run tests
run: python manage.py test
env:
Expand Down
130 changes: 130 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -164,3 +164,133 @@ staticfiles/

# macOS stuff:
.DS_Store
# Logs
logs
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*
lerna-debug.log*
.pnpm-debug.log*

# Diagnostic reports (https://nodejs.org/api/report.html)
report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json

# Runtime data
pids
*.pid
*.seed
*.pid.lock

# Directory for instrumented libs generated by jscoverage/JSCover
lib-cov

# Coverage directory used by tools like istanbul
coverage
*.lcov

# nyc test coverage
.nyc_output

# Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files)
.grunt

# Bower dependency directory (https://bower.io/)
bower_components

# node-waf configuration
.lock-wscript

# Compiled binary addons (https://nodejs.org/api/addons.html)
build/Release

# Dependency directories
node_modules/
jspm_packages/

# Snowpack dependency directory (https://snowpack.dev/)
web_modules/

# TypeScript cache
*.tsbuildinfo

# Optional npm cache directory
.npm

# Optional eslint cache
.eslintcache

# Optional stylelint cache
.stylelintcache

# Microbundle cache
.rpt2_cache/
.rts2_cache_cjs/
.rts2_cache_es/
.rts2_cache_umd/

# Optional REPL history
.node_repl_history

# Output of 'npm pack'
*.tgz

# Yarn Integrity file
.yarn-integrity

# dotenv environment variable files
.env
.env.development.local
.env.test.local
.env.production.local
.env.local

# parcel-bundler cache (https://parceljs.org/)
.cache
.parcel-cache

# Next.js build output
.next
out

# Nuxt.js build / generate output
.nuxt
dist

# Gatsby files
.cache/
# Comment in the public line in if your project uses Gatsby and not Next.js
# https://nextjs.org/blog/next-9-1#public-directory-support
# public

# vuepress build output
.vuepress/dist

# vuepress v2.x temp and cache directory
.temp
.cache

# Docusaurus cache and generated files
.docusaurus

# Serverless directories
.serverless/

# FuseBox cache
.fusebox/

# DynamoDB Local files
.dynamodb/

# TernJS port file
.tern-port

# Stores VSCode versions used for testing VSCode extensions
.vscode-test

# yarn v2
.yarn/cache
.yarn/unplugged
.yarn/build-state.yml
.yarn/install-state.gz
.pnp.*
3 changes: 2 additions & 1 deletion .tool-versions
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
python 3.12.2
python 3.12.3
nodejs 20.13.1
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ So far, I'm liking both. For projects like this one, HTMX is a keeper. `htpy` ha
For code cleanliness, we also use:

- [Ruff](https://github.com/astral-sh/ruff) for linting
- [mypy](https://mypy-lang.org/) for type checking
- [pyright](https://github.com/microsoft/pyright) for type checking

### Getting a local dev instance running

Expand Down
46 changes: 46 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

10 changes: 10 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
{
"name": "voterbowl",
"version": "0.1.0",
"description": "Encouraging voters to check early and check often",
"author": "Dave Peck <dave@frontseat.org>",
"license": "MIT",
"devDependencies": {
"pyright": "^1.1.363"
}
}
11 changes: 5 additions & 6 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -12,12 +12,11 @@ classifiers = [
"Programming Language :: Python :: 3.12",
]

[tool.mypy]
exclude = [".venv", "migrations"]
ignore_missing_imports = true
disallow_untyped_defs = false
show_error_codes = true
plugins = ["mypy_django_plugin.main"]
[tool.pyright]
include = ["server/**/*.py"]
exclude = ["**/migrations/**"]
typeCheckingMode = "basic"
useLibraryCodeForTypes = true

[tool.django-stubs]
django_settings_module = "server.settings"
Expand Down
1 change: 0 additions & 1 deletion requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@ gunicorn>=21,<22
htpy>=24.4.0
httpx>=0.20.0
markdown>=3.6.0
mypy>=1.9.0
pillow>=10.2.0
psycopg[binary]>=3,<4
pydantic>=2.7.0
Expand Down
6 changes: 3 additions & 3 deletions scripts/test.sh
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,9 @@ ruff format --check server
printf "${BLUE}Running ruff...${NC}\n"
ruff check server

# Run the Python type checker (mypy).
printf "${BLUE}Running mypy...${NC}\n"
mypy server
# Run the Python type checker (pyright).
printf "${BLUE}Running pyright...${NC}\n"
npx pyright

# Run Python tests
printf "${BLUE}Running tests...${NC}\n"
Expand Down
6 changes: 2 additions & 4 deletions server/utils/agcod.py
Original file line number Diff line number Diff line change
Expand Up @@ -189,10 +189,8 @@ def post_json_rpc(
return self.post_json(url, data, headers=headers)


# I would use `type StatusCode = ...` except mypy still has an open issue
# for supporting PEP 695. Ugh; all the other type checkers support it!
StatusCode: t.TypeAlias = t.Literal["SUCCESS", "FAILURE", "RESEND"]
CardStatus: t.TypeAlias = t.Literal["Fulfilled", "RefundedToPurchaser", "Expired"]
type StatusCode = t.Literal["SUCCESS", "FAILURE", "RESEND"]
type CardStatus = t.Literal["Fulfilled", "RefundedToPurchaser", "Expired"]


class BaseCamelModel(p.BaseModel):
Expand Down
8 changes: 1 addition & 7 deletions server/utils/components.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,14 +37,8 @@ def css_vars(**vars: str) -> str:
return " ".join(f"--{k.replace('_', '-')}: {v};" for k, v in vars.items())


# FUTURE use PEP 695 syntax when mypy supports it
P = t.ParamSpec("P")
C = t.TypeVar("C")
R = t.TypeVar("R", h.Element, h.Node)


@dataclass(frozen=True)
class with_children(t.Generic[C, P, R]):
class with_children[C, R: (h.Element, h.Node), **P]:
"""Wrap a function to make it look more like an htpy.Element."""

_f: t.Callable[t.Concatenate[C, P], R]
Expand Down
10 changes: 7 additions & 3 deletions server/vb/management/commands/enter_contest.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
import typing as t

from django.core.management.base import BaseCommand

from server.vb.models import Contest, School
from server.vb.models import Contest, School, Student
from server.vb.ops import enter_contest, send_validation_link_email


Expand All @@ -27,9 +29,11 @@ def handle(self, contest_id, emails: list[str], **options):
def _process_students(self, contest: Contest, school: School, email: str):
"""Enter one or more students into a contest, if not already entered."""
if email[0] == "@":
students = school.students.filter(email__endswith=email[1:])
students = t.cast(
t.Iterable[Student], school.students.filter(email__endswith=email[1:])
)
else:
students = school.students.filter(email=email)
students = t.cast(t.Iterable[Student], school.students.filter(email=email))

when = contest.start_at

Expand Down
4 changes: 3 additions & 1 deletion server/vb/management/commands/get_available_funds.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import typing as t

from django.core.management.base import BaseCommand

from server.utils.agcod import AGCODClient
Expand All @@ -10,7 +12,7 @@ class Command(BaseCommand):
"Get available funds remaining for generating gift codes using the AGCOD API."
)

def handle(self, **options):
def handle(self, **options: t.Any):
"""Handle the command."""
client = AGCODClient.from_settings()
try:
Expand Down
12 changes: 6 additions & 6 deletions server/vb/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -183,26 +183,26 @@ class ContestKind(models.TextChoices):
"""The various kinds of contests."""

# Every student wins a prize (gift card; charitable donation; etc.)
GIVEAWAY = "giveaway", "Giveaway"
GIVEAWAY = "giveaway", "Giveaway" # type: ignore

# Every student rolls a dice; some students win a prize.
DICE_ROLL = "dice_roll", "Dice roll"
DICE_ROLL = "dice_roll", "Dice roll" # type: ignore

# A single student wins a prize after the contest ends.
SINGLE_WINNER = "single_winner", "Single winner"
SINGLE_WINNER = "single_winner", "Single winner" # type: ignore

# No prizes are awarded.
NO_PRIZE = "no_prize", "No prize"
NO_PRIZE = "no_prize", "No prize" # type: ignore


class ContestWorkflow(models.TextChoices):
"""The various workflows for contests."""

# Issue an amazon gift card and email automatically
AMAZON = "amazon", "Amazon"
AMAZON = "amazon", "Amazon" # type: ignore

# No automated workflow; manual intervention may be required
NONE = "none", "None"
NONE = "none", "None" # type: ignore


class Contest(models.Model):
Expand Down

0 comments on commit bd44458

Please sign in to comment.