Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Configure ruff and UV #23

Merged
merged 12 commits into from
Nov 7, 2024
Merged
17 changes: 13 additions & 4 deletions .github/workflows/lint.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -6,21 +6,30 @@ on:

name: lint

env:
PYTHONUNBUFFERED: "1"

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[Question, non-blocking] Should we be setting this more widely across our workflows? I've never noticed a buffering problem with Python scripts in GitHub workflow logs (except on our network, which blocks websockets and so prevents all real-time logs from displaying) but maybe I just haven't been watching out for the right issue.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's purely defensive. I've run into a few cases over the past couple months where Python won't output full logs due to its buffered output.

UV_SYSTEM_PYTHON: 1

jobs:
lint-ruff:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4

- name: Setup python
- name: Install uv
uses: astral-sh/setup-uv@v3
with:
enable-cache: true
cache-dependency-glob: pyproject.toml

- name: Setup Python
uses: actions/setup-python@v5
with:
python-version: "3.x"
cache: "pip"
python-version-file: pyproject.toml
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This just install the min version specified in our pyproject.toml, in this case 3.10.


- name: Install dependencies
run: pip install . ruff
run: uv pip install . ruff

- name: Lint with ruff
run: ruff check --output-format=github .
23 changes: 16 additions & 7 deletions .github/workflows/pages.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,10 @@ on:

name: pages

env:
PYTHONUNBUFFERED: "1"
UV_SYSTEM_PYTHON: 1

jobs:
build-sphinx-site:
runs-on: ubuntu-latest
Expand All @@ -17,18 +21,23 @@ jobs:
- name: Configure pages
uses: actions/configure-pages@v5

- name: Setup python
- name: Install uv
uses: astral-sh/setup-uv@v3
with:
enable-cache: true
cache-dependency-glob: pyproject.toml
cache-suffix: docs

- name: Setup Python
uses: actions/setup-python@v5
with:
python-version: "3.x"
cache: "pip"
python-version-file: pyproject.toml

- name: Install dependencies
run: pip install .[docs]
- name: Install Python dependencies
run: uv pip install .[docs]

- name: Generate HTML
run: |
sphinx-build -d _build/doctrees docs/source _build/html
run: sphinx-build -d _build/doctrees docs/source _build/html

- name: Upload artifact
uses: actions/upload-pages-artifact@v3
Expand Down
4 changes: 4 additions & 0 deletions .github/workflows/pre-commit.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,10 @@ on:

name: pre-commit

env:
PYTHONUNBUFFERED: "1"
UV_SYSTEM_PYTHON: 1

jobs:
pre-commit:
runs-on: ubuntu-latest
Expand Down
18 changes: 14 additions & 4 deletions .github/workflows/pypi-publish.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,10 @@ on:

name: pypi-publish

env:
PYTHONUNBUFFERED: "1"
UV_SYSTEM_PYTHON: 1

jobs:
pypi-publish:
name: pypi-publish
Expand All @@ -18,16 +22,22 @@ jobs:
- name: Checkout
uses: actions/checkout@v4

- name: Install uv
uses: astral-sh/setup-uv@v3
with:
enable-cache: true
cache-dependency-glob: pyproject.toml

- name: Setup Python
uses: actions/setup-python@v5
with:
python-version: "3.x"
python-version-file: pyproject.toml

- name: Install dependencies
run: pip install build
- name: Install Python dependencies
run: uv pip install .

- name: Build Python dist
run: python -m build
run: uv build

- name: Publish package distributions to PyPI
uses: pypa/gh-action-pypi-publish@release/v1
18 changes: 14 additions & 4 deletions .github/workflows/python-package.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -18,14 +18,24 @@ jobs:
- name: Checkout
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Note that usually we want to install Python packages into the system library with uv by setting UV_SYSTEM_PYTHON=1. However, in this case, we want uv to install things into the tox-created virtualenv, so we leave that var unset.

uses: actions/checkout@v4

- name: Setup python ${{ matrix.python-version }}
- name: Install uv
uses: astral-sh/setup-uv@v3
with:
enable-cache: true
cache-dependency-glob: pyproject.toml

- name: Setup Python ${{ matrix.python-version }}
uses: actions/setup-python@v5
with:
python-version: ${{ matrix.python-version }}
cache: "pip"

- name: Install tox
run: pip install tox-gh>=1.3.0
shell: bash
run: |
uv tool install tox --with tox-uv
tox --version
Comment on lines +35 to +36
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The tox-uv plugin sets up virtual environments using uv that are compatible with tox.


- name: Test install with tox
run: tox -v --notest
run: |
env=$(echo ${{ matrix.python-version }} | tr -d '.' | sed 's/^/py/')
tox r -v --notest -e "$env"
16 changes: 13 additions & 3 deletions .github/workflows/test-coverage.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,10 @@ on:

name: test-coverage

env:
PYTHONUNBUFFERED: "1"
UV_SYSTEM_PYTHON: 1

jobs:
test-coverage:
runs-on: ubuntu-latest
Expand All @@ -18,14 +22,20 @@ jobs:
- name: Checkout
uses: actions/checkout@v4

- name: Setup python ${{ matrix.python-version }}
- name: Install uv
uses: astral-sh/setup-uv@v3
with:
enable-cache: true
cache-dependency-glob: pyproject.toml
cache-suffix: test

- name: Setup Python ${{ matrix.python-version }}
uses: actions/setup-python@v5
with:
python-version: ${{ matrix.python-version }}
cache: "pip"

- name: Install dependencies
run: pip install .[test]
run: uv pip install .[test]

- name: Run pytest
run: |
Expand Down
4 changes: 3 additions & 1 deletion .pre-commit-config.yaml
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
repos:
- repo: https://github.com/astral-sh/ruff-pre-commit
rev: v0.5.5
rev: v0.7.2
hooks:
- id: ruff
args:
- --fix
- id: ruff-format
8 changes: 4 additions & 4 deletions assesspy/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,19 +6,19 @@
from .formulas import (
cod,
cod_met,
ki,
mki,
mki_met,
prb,
prb_met,
prd,
prd_met,
ki,
mki,
mki_met,
)
from .load_data import ratios_sample
from .outliers import (
iqr_outlier,
is_outlier,
quantile_outlier,
)
from .load_data import ratios_sample
from .sales_chasing import detect_chasing
from .utils import check_inputs
10 changes: 6 additions & 4 deletions assesspy/ci.py
Original file line number Diff line number Diff line change
Expand Up @@ -54,10 +54,12 @@ def boot_ci(fun, nboot=100, alpha=0.05, **kwargs):
kwargs.keys()
):
kwargs = (kwargs["assessed"], kwargs["sale_price"])
elif fun.__name__ == "prd" and not set(["assessed", "sale_price"]).issubset(
kwargs.keys()
):
raise Exception("PRD function expects argurments 'assessed' and 'sale_price'.")
elif fun.__name__ == "prd" and not set(
["assessed", "sale_price"]
).issubset(kwargs.keys()):
raise Exception(
"PRD function expects argurments 'assessed' and 'sale_price'."
)
else:
kwargs = tuple(kwargs.values())

Expand Down
5 changes: 4 additions & 1 deletion assesspy/formulas.py
Original file line number Diff line number Diff line change
Expand Up @@ -165,7 +165,10 @@ def prb(assessed, sale_price, round=None):
prb_ci = prb_model.conf_int(alpha=0.05)[0].tolist()

if round is not None:
out = {"prb": np.round(prb_val, round), "95% ci": np.round(prb_ci, round)}
out = {
"prb": np.round(prb_val, round),
"95% ci": np.round(prb_ci, round),
}

else:
out = {"prb": prb_val, "95% ci": prb_ci}
Expand Down
4 changes: 3 additions & 1 deletion assesspy/load_data.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,5 +23,7 @@ def ratios_sample():
:rtype: DataFrame
"""

stream = pkg_resources.resource_stream(__name__, "data/ratios_sample.parquet")
stream = pkg_resources.resource_stream(
__name__, "data/ratios_sample.parquet"
)
return pd.read_parquet(stream)
4 changes: 3 additions & 1 deletion assesspy/outliers.py
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,9 @@ def is_outlier(x, method="iqr", probs=[0.05, 0.95]):
ap.is_outlier(ap.ratios_sample().ratio)
"""

out = {"iqr": iqr_outlier(x), "quantile": quantile_outlier(x, probs)}.get(method)
out = {"iqr": iqr_outlier(x), "quantile": quantile_outlier(x, probs)}.get(
method
)

# Warn about removing data from small samples, as it can severely distort
# ratio study outcomes
Expand Down
4 changes: 3 additions & 1 deletion assesspy/sales_chasing.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,9 @@ def detect_chasing_cdf(ratio, bounds=[0.98, 1.02], cdf_gap=0.03):
# Check if the largest difference is greater than the threshold and make
# sure it's within the specified boundaries
diff_loc = sorted_ratio[np.argmax(diffs)]
out = (max(diffs) > cdf_gap) & ((diff_loc > bounds[0]) & (diff_loc < bounds[1]))
out = (max(diffs) > cdf_gap) & (
(diff_loc > bounds[0]) & (diff_loc < bounds[1])
)

return out

Expand Down
4 changes: 3 additions & 1 deletion assesspy/tests/test_formulas.py
Original file line number Diff line number Diff line change
Expand Up @@ -162,7 +162,9 @@ def test_prb_met(self): # Standard met function
gini_data_sale.append(first_column)
gini_data_assessed.append(second_column)

gini_data_assessed = [int(value.replace('"', "")) for value in gini_data_assessed]
gini_data_assessed = [
int(value.replace('"', "")) for value in gini_data_assessed
]
gini_data_sale = [int(value.replace('"', "")) for value in gini_data_sale]

mki_out = assesspy.mki(gini_data_assessed, gini_data_sale)
Expand Down
1 change: 1 addition & 0 deletions docs/source/conf.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import os
import sys

from sphinx_pyproject import SphinxConfig

sys.path.append(os.path.abspath("../.."))
Expand Down
Loading