Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 7 additions & 5 deletions .github/workflows/config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ jobs:
runs-on: ubuntu-latest
strategy:
matrix:
python-version: ['3.8', '3.12']
python-version: ['3.12']
container: python:${{ matrix.python-version }}-slim
services:
redis:
Expand Down Expand Up @@ -44,12 +44,12 @@ jobs:
path: |
.tox
.mypy_cache
key: ${{ runner.os }}-build-${{ env.cache-name }}-tox-${{ hashFiles('**/requirements/base.txt') }}-${{ hashFiles('**/requirements/testing.txt') }}-${{ hashFiles('**/requirements/development.txt') }}-${{ hashFiles('**/requirements/production.txt') }}
key: ${{ runner.os }}-build-${{ env.cache-name }}-tox-${{ hashFiles('**/pyproject.toml') }}

- name: install tox
run: |
python3 --version
pip3 install tox
pip3 install tox tox-uv

- name: Run tests
env:
Expand All @@ -71,6 +71,8 @@ jobs:
sentry:
if: ${{ github.ref == 'refs/heads/master' }}
runs-on: ubuntu-latest
env:
SENTRY_ORG: pyslackers
steps:
- name: Checkout repository
uses: actions/checkout@v4
Expand All @@ -81,8 +83,8 @@ jobs:
uses: getsentry/action-release@v3
env:
SENTRY_AUTH_TOKEN: ${{ secrets.SENTRY_AUTH_TOKEN }}
SENTRY_ORG: pyslackers
SENTRY_PROJECT: website
SENTRY_ORG: ${{ secrets.SENTRY_ORG }}
SENTRY_PROJECT: ${{ secrets.SENTRY_PROJECT }}
with:
environment: production
release: ${{ github.sha }}
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -9,3 +9,4 @@ artifacts/
# Editors
.idea/
.vscode
requirements.txt
4 changes: 3 additions & 1 deletion .platform.app.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,9 @@ web:
# The hooks executed at various points in the lifecycle of the application.
hooks:
build: |
pip install -r requirements/production.txt
pip install uv
make gen-requirements
pip install -r requirements.txt
deploy: |
alembic upgrade head

Expand Down
1 change: 0 additions & 1 deletion .pylintrc
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@ init-import=yes

[MESSAGES CONTROL]
disable =
bad-continuation,
fixme,
missing-docstring,
invalid-name,
Expand Down
48 changes: 23 additions & 25 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,9 @@
* [Docker](https://www.docker.com/get-started)
* [docker-compose](https://docs.docker.com/compose)
* Python 3.8 or 3.12
* tox
* [pipx](https://pypa.github.io/pipx/) - Install Python applications in isolated environments
* [uv](https://github.com/astral-sh/uv) - Fast Python package installer and resolver
* tox (install via `pipx install tox`)

## Quickstart

Expand Down Expand Up @@ -49,46 +51,42 @@ If instead you'd prefer to set-up your project on the host machine, you are free

If you have [`pyenv`](https://github.com/pyenv/pyenv) installed already, the [python version](.python-version) should be set automatically for you based on the `.python-version` file. However if you do not, you should make sure that Python 3.8 or 3.12 is available on your host.

### 2. Virtual Environment
### 2. Install uv and Setup Environment

For a host of reasons that are covered elsewhere, you should never install dependencies to your "system python". Instead you should set up a "virtual environment".
We use `uv` for fast Python package management. It will automatically create a virtual environment and install dependencies.

```bash
$ python -V
Python 3.8.16 # or Python 3.12.x
$ python -m venv .venv
```

You will now have a directory in your project called `.venv`, which is ignored by source control as it is not portable. You need to activate this _per shell instance_.
# Install uv if you haven't already
$ curl -LsSf https://astral.sh/uv/install.sh | sh

```bash
$ source .venv/bin/activate
# Sync dependencies (creates venv and installs all dependencies)
$ uv sync --group dev
```

Now your shell should have the virtual environment's name prepended:
This creates a `.venv` directory with all dependencies installed. You can either activate the virtual environment or use `uv run`:

```bash
(.venv) $
```

You can check that the python and pip paths are as expected, the `.venv` directory in your current working directory:
# Option 1: Activate venv and run commands normally
$ source .venv/bin/activate
(.venv) $ python --version

```bash
(.venv) $ which python
${PWD}/.venv/bin/python
(.venv) $ which pip
${PWD}/.venv/bin/pip
# Option 2: Use uv run (recommended)
$ uv run python --version
```

### 3. Dependencies

Now you need the dependencies installed, which is as simple as:
Dependencies are already installed when you run `uv sync`. To add new dependencies:

```bash
(.venv) $ pip install -r requirements/development.txt
# Add a production dependency
$ uv add package-name

# Add a dev dependency to a specific group
$ uv add --group test pytest-new-plugin
```

OR run the above steps with the following make command:
OR use the make command for initial setup:

```bash
$ make install
Expand Down Expand Up @@ -120,7 +118,7 @@ Once that launches you can visit [localhost:8000](http://localhost:8000) in your
To run the tests with automatic setup and teardown of services simply run:

```bash
# Ensure the virtualenv is activated i.e source .venv/bin/activate
# Use uv run to execute commands or activate the venv with: source .venv/bin/activate
tox
```

Expand Down
7 changes: 4 additions & 3 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
FROM python:3.8.1-alpine
FROM python:3.12-alpine
WORKDIR /app

ENV PORT=8000 \
Expand All @@ -11,9 +11,10 @@ RUN apk add --no-cache tzdata gcc g++ make postgresql-dev build-base git && \
echo "UTC" >> /etc/timezone && \
apk del tzdata

COPY requirements requirements
# Copy just the dependency specification first for better caching
COPY pyproject.toml ./
RUN pip install -U pip && \
pip install -r requirements/development.txt
pip install .

COPY . .

Expand Down
29 changes: 13 additions & 16 deletions Makefile
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
.PHONY: clean install formatter lint types static-checks test up down setup_services start_app
.PHONY: clean install formatter lint types static-checks test up down setup_services start_app gen-requirements
help:
@echo "make"
@echo " clean"
Expand All @@ -21,8 +21,10 @@ help:
@echo " Stop the services"
@echo " setup_services"
@echo " Setup required services using tox."
@echo " start_server"
@echo " start_app"
@echo " Start the gunicorn server."
@echo " gen-requirements"
@echo " Generate requirements.txt from uv.lock for deployment."

clean:
find . -name '*.pyc' -exec rm -f {} +
Expand All @@ -32,39 +34,34 @@ clean:
rm -rf .venv/

install:
python3 -m venv .venv
.venv/bin/pip3 install -U pip
.venv/bin/pip3 install -r ./requirements/development.txt
uv sync --group dev

formatter:
tox -e autoformat

format: formatter

lint:
tox -e lint
tox -e py312-lint

types:
tox -e mypy
tox -e py312-mypy

static-checks: lint types

test:
@if python3.12 --version >/dev/null 2>&1; then \
tox -e py312-test; \
elif python3.8 --version >/dev/null 2>&1; then \
tox -e py38-test; \
else \
echo "Neither Python 3.8 nor 3.12 found"; exit 1; \
fi
tox -e py312-test

up: setup_services start_server
up: setup_services start_app

setup_services:
tox -e setup_services

start_app:
.venv/bin/gunicorn --bind 127.0.0.1:8000 --worker-class aiohttp.GunicornWebWorker --reload pyslackersweb:app_factory
uv run gunicorn --bind 127.0.0.1:8000 --worker-class aiohttp.GunicornWebWorker --reload pyslackersweb:app_factory

down:
tox -e teardown_services

gen-requirements:
uv export --no-hashes --format requirements-txt > requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
Create Date: 2020-01-24 02:59:46.288204+00:00

"""

from alembic import op
import sqlalchemy as sa

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
Create Date: 2020-01-25 03:06:18.331532+00:00

"""

from alembic import op
import sqlalchemy as sa

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
Create Date: 2020-07-13 13:12:47.561813+00:00

"""

from alembic import op
import sqlalchemy as sa

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
Create Date: 2021-01-04 20:23:50.479134+00:00

"""

from datetime import datetime

from alembic import op
Expand Down
55 changes: 54 additions & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,59 @@
[project]
name = "pyslackersweb"
version = "0.1.0"
description = "The website for the pythondev.slack.com community"
requires-python = ">=3.8"
dependencies = [
# Web Framework
"aiohttp==3.10.11",
"aiohttp-jinja2==1.5",
"aiohttp-oauth2==0.0.3",
"aiohttp-remotes==1.0.0",
# Database
"asyncpgsa==0.27.1",
"sqlalchemy==1.3.23",
"psycopg2-binary==2.9.10",
"alembic==1.5.8",
# Cache/Redis
"aioredis==1.3.1",
# Task Scheduling
"apscheduler>=3.10.0",
# Slack Integration
"slack-sansio>=1.1.0",
# Server
"gunicorn>=22.0.0",
"uvloop==0.21.0",
# Utilities
"marshmallow==3.22.0",
"sentry-sdk==1.0.0",
"pyyaml==6.0.2",
"urllib3<2.0",
]

[dependency-groups]
test = [
"pytest>=7.0.0",
"pytest-cov>=4.1.0",
"pytest-aiohttp>=1.0.4",
"pytest-asyncio>=0.23.5",
]
lint = [
"black>=24.1.1",
"pylint>=3.0.0",
]
typing = [
"mypy>=1.8.0",
"types-pyyaml>=6.0.12.20241230",
]
dev = [
{ include-group = "test" },
{ include-group = "lint" },
{ include-group = "typing" },
]

[tool.black]
line-length = 100
target-version = ["py38", "py312"]
target-version = ["py312"]
exclude = """
/(
\\.git
Expand Down
4 changes: 2 additions & 2 deletions pyslackersweb/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,13 +22,13 @@


logging_configuration = pathlib.Path(__file__).parents[1] / "logging.yml"
with open(logging_configuration, "r") as f:
with open(logging_configuration, "r", encoding="utf-8") as f:
config = yaml.safe_load(f)

logging.config.dictConfig(config)
logging.captureWarnings(True)

sentry_sdk.init(
sentry_sdk.init( # type: ignore[abstract]
dsn=settings.SENTRY_DSN,
integrations=[
AioHttpIntegration(),
Expand Down
17 changes: 0 additions & 17 deletions requirements/base.txt

This file was deleted.

3 changes: 0 additions & 3 deletions requirements/development.txt

This file was deleted.

1 change: 0 additions & 1 deletion requirements/production.txt

This file was deleted.

11 changes: 0 additions & 11 deletions requirements/testing.txt

This file was deleted.

Loading
Loading