Skip to content
Open
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
1 change: 1 addition & 0 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ repos:
- id: trailing-whitespace
- id: end-of-file-fixer
- id: check-yaml
exclude: "^conda/recipe/"
- id: check-added-large-files
- id: check-ast

Expand Down
1 change: 1 addition & 0 deletions MANIFEST.in
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
recursive-include pysus *.c *.h
include pysus/utilities/*
include requirements.txt
include LICENSE
13 changes: 13 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -266,6 +266,19 @@ conda activate pysus
poetry install
```

### Conda-Forge Recipe

The conda recipe is **auto-generated from `pyproject.toml`**. After releasing a new version,
checkout to the main branch and run:

```bash
python conda/generate_recipe.py
```

This reads `pyproject.toml` and writes `conda/recipe/meta.yaml` with the correct conda-forge package names, version constraints, and the current SHA256 (fetched from PyPI). Never edit `meta.yaml` by hand.

To submit or update the recipe, copy it into the [pysus-feedstock](https://github.com/conda-forge/pysus-feedstock) repo.

### Running Tests

Run code linters:
Expand Down
162 changes: 162 additions & 0 deletions conda/generate_recipe.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,162 @@
#!/usr/bin/env python3
"""Generate conda v1 recipe (recipe.yaml) via grayskull.

Usage:
python conda/generate_recipe.py

Runs ``grayskull pypi --strict-conda-forge --use-v1-format pysus``,
patches the result with project-specific overrides, and writes to
``conda/recipe/recipe.yaml``.
"""

import re
import subprocess
import sys
import tempfile
from pathlib import Path

from ruamel.yaml import YAML

RECIPE_PATH = Path(__file__).resolve().parent / "recipe" / "recipe.yaml"

MAINTAINERS: list[str] = [
"fccoelho",
"luabida",
"esloch",
]

CONDA_NAME_MAP: dict[str, str] = {
"wget": "python-wget",
"dotenv": "python-dotenv",
"python-duckdb": "duckdb",
}

SKIP_DEPS: set[str] = {
"python-magic-bin",
}

CONDA_VERSION_OVERRIDES: dict[str, str] = {
"duckdb-engine": ">=0.15.0",
}

yaml = YAML()
yaml.preserve_quotes = True
yaml.indent(mapping=2, sequence=4, offset=2)


def strip_post(spec: str) -> str:
return re.sub(r"\.post\d+", "", spec)


def drop_upper_bounds(spec: str) -> str:
return re.sub(r"\s*,?\s*<[=>]?\s*[\d.*]+", "", spec)


def drop_trailing_dotstar(spec: str) -> str:
return re.sub(r"\.\*$", "", spec)


def clean_constraint(spec: str) -> str:
spec = strip_post(spec)
spec = drop_upper_bounds(spec)
return spec


def patch_recipe(recipe: dict) -> dict:
version = recipe["context"]["version"]
recipe["context"] = {"version": version, "python_min": "3.11"}

about = recipe["about"]
about["homepage"] = "https://github.com/AlertaDengue/PySUS"
about["documentation"] = "https://pysus.readthedocs.io/"
about["repository"] = "https://github.com/AlertaDengue/PySUS"
about["description"] = (
"PySUS is a Python package for downloading, parsing, and analyzing "
"Brazil's Public Health data (DATASUS). It provides tools for "
"fetching data from various sources including FTP servers, DadosGov "
"API, and DuckLake (S3-based catalog), also contains utilities for "
"reading DBC/DBF file formats present in DATASUS."
)

recipe["extra"]["recipe-maintainers"] = MAINTAINERS

recipe["requirements"]["host"] = [
"python ${{ python_min }}.*" if d.startswith("python ") else d
for d in recipe["requirements"]["host"]
]

run_deps: list[str] = recipe["requirements"]["run"]
new_run: list[str] = []
for dep in run_deps:
parts = dep.split(None, 1)
name = parts[0]
constraint = parts[1] if len(parts) > 1 else ""

if name == "python":
new_run.append("python >=${{ python_min }}")
continue

if name in SKIP_DEPS:
continue

if name in CONDA_NAME_MAP:
name = CONDA_NAME_MAP[name]

if name in CONDA_VERSION_OVERRIDES:
constraint = CONDA_VERSION_OVERRIDES[name]
elif constraint:
constraint = clean_constraint(constraint)

new_run.append(f"{name} {constraint}".strip())

recipe["requirements"]["run"] = new_run

recipe["tests"] = [
{
"python": {
"imports": ["pysus"],
"pip_check": True,
"python_version": [
"${{ python_min }}.*",
"*",
],
},
"requirements": {"run": ["pip"]},
"script": ["pysus --help"],
}
]

return recipe


def main() -> None:
with tempfile.TemporaryDirectory() as tmpdir:
result = subprocess.run(
[
"grayskull",
"pypi",
"--strict-conda-forge",
"--use-v1-format",
"pysus",
"-o",
tmpdir,
],
capture_output=True,
text=True,
)
if result.returncode != 0:
print(result.stderr, file=sys.stderr)
sys.exit(1)

src = Path(tmpdir) / "pysus" / "recipe.yaml"
recipe = yaml.load(src.read_text())
recipe = patch_recipe(recipe)

RECIPE_PATH.parent.mkdir(parents=True, exist_ok=True)
with open(RECIPE_PATH, "w") as fh:
yaml.dump(recipe, fh)
print(f"Wrote {RECIPE_PATH}")


if __name__ == "__main__":
main()
86 changes: 86 additions & 0 deletions conda/recipe/recipe.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
schema_version: 1

context:
version: "2.6.3"
python_min: '3.11'
package:
name: pysus
version: ${{ version }}

source:
url: https://pypi.org/packages/source/p/pysus/pysus-${{ version }}.tar.gz
sha256: e537a501befafc055406f3ed820175e863f95033189a4a2c130ade18087bc076

build:
number: 0
noarch: python
script: ${{ PYTHON }} -m pip install . -vv --no-deps --no-build-isolation
python:
entry_points:
- pysus = pysus.cli:app

requirements:
host:
- python ${{ python_min }}.*
- poetry-core
- pip
run:
- python >=${{ python_min }}
- python-dateutil 2.8.2.*
- fastparquet >=2023.10.1
- pyarrow >=11.0.0
- numpy >=2.4.0
- tqdm >=4.67.0
- python-wget >=3.2.0
- loguru >=0.6.0
- unidecode >=1.3.6
- dateparser >=1.1.8
- pandas >=2.2.2
- typing_extensions >=4.10.0
- pydantic >=2.12.5
- duckdb >=1.4.4
- duckdb-engine >=0.15.0
- sqlalchemy >=2.0.48
- python-magic
- chardet >=7.4.0
- anyio >=4.13.0
- httpx >=0.28.0
- aioftp >=0.21.4
- dbfread 2.0.7.*
- bigtree >=0.12.2
- pyreaddbc >=2.0.4
- python-dotenv >=0.9.9
- boto3 >=1.42.89
- typer >=0.24.1
- humanize >=4.8.0
tests:
- python:
imports:
- pysus
pip_check: true
python_version:
- ${{ python_min }}.*
- '*'
requirements:
run:
- pip
script:
- pysus --help
about:
summary: Tools for dealing with Brazil's Public health data
license: GPL-3.0-only
license_file: LICENSE
homepage: https://github.com/AlertaDengue/PySUS

documentation: https://pysus.readthedocs.io/
repository: https://github.com/AlertaDengue/PySUS
description: PySUS is a Python package for downloading, parsing, and analyzing
Brazil's Public Health data (DATASUS). It provides tools for fetching data
from various sources including FTP servers, DadosGov API, and DuckLake
(S3-based catalog), also contains utilities for reading DBC/DBF file formats
present in DATASUS.
extra:
recipe-maintainers:
- fccoelho
- luabida
- esloch
3 changes: 2 additions & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,8 @@ name = "pysus"
version = "2.6.3" # changed by semantic-release
description = "Tools for dealing with Brazil's Public health data"
authors = ["Flavio Codeco Coelho <fccoelho@gmail.com>", "Luã Bida Vacaro <luabidaa@gmail.com>"]
license = "GPL"
license = "GPL-3.0-only"
readme = "README.md"

packages = [{ include = "pysus"}]

Expand Down
Loading