Skip to content

Commit 5f8269f

Browse files
authored
feat(package): remove the need of the libffi-dev requirement on pyreaddbc (#278)
* feat(package): remove the need of the libffi-dev requirement on pyreaddbc * chore(CI): remove the conda installation from the runners * fix: apparently MIME magic typing doesnt work on Windows OS
1 parent 5cb4058 commit 5f8269f

12 files changed

Lines changed: 2761 additions & 2346 deletions

File tree

.github/workflows/python-package.yml

Lines changed: 9 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ on: [push, pull_request]
55
jobs:
66
tests:
77
runs-on: ${{ matrix.os }}
8-
timeout-minutes: ${{ (matrix.os == 'windows-latest' && 30) || 15 }}
8+
timeout-minutes: ${{ (matrix.os == 'windows-latest' && 45) || 15 }}
99

1010
strategy:
1111
matrix:
@@ -19,22 +19,17 @@ jobs:
1919
steps:
2020
- uses: actions/checkout@v4
2121

22-
- uses: conda-incubator/setup-miniconda@v3
22+
- uses: actions/setup-python@v5
2323
with:
24-
miniforge-version: latest
25-
environment-file: conda/dev.yaml
26-
channels: conda-forge,nodefaults
27-
activate-environment: pysus
28-
auto-update-conda: true
29-
conda-solver: libmamba
24+
python-version: ${{ matrix.python-version }}
3025

3126
- name: Install dependencies
32-
shell: bash -l {0}
27+
shell: bash
3328
run: |
3429
pip install poetry poetry-plugin-export
3530
poetry config virtualenvs.create false
3631
if [ "${{ runner.os }}" = "Linux" ]; then
37-
poetry install --without dev --extras dbc
32+
poetry install --without dev
3833
pip install pre-commit
3934
else
4035
poetry install --without dev
@@ -43,21 +38,21 @@ jobs:
4338
4439
- name: Linting
4540
if: matrix.os == 'ubuntu-latest'
46-
shell: bash -l {0}
41+
shell: bash
4742
run: pre-commit run --files pysus/**/*
4843

4944
- name: Tests (Linux)
5045
if: matrix.os != 'windows-latest'
51-
shell: bash -l {0}
46+
shell: bash
5247
run: |
5348
poetry run pytest -vv pysus/tests/ --retries 3 --retry-delay 15 --cov=pysus --cov-report=xml:coverage.xml --cov-report=term-missing
5449
5550
- name: Tests (Windows)
5651
if: matrix.os == 'windows-latest'
57-
shell: bash -l {0}
52+
shell: bash
5853
run: |
5954
export DUCKDB_NO_THREADS=1
60-
poetry run pytest -vv pysus/tests/ --retries 3 --retry-delay 15 --timeout=480 -p no:cacheprovider -p no:asyncio
55+
poetry run pytest -vv pysus/tests/ --timeout=480 -p no:cacheprovider
6156
6257
- name: Upload coverage to Codecov
6358
if: matrix.os == 'ubuntu-latest'

.github/workflows/release.yaml

Lines changed: 3 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -84,25 +84,17 @@ jobs:
8484
if: ${{ !startsWith(github.ref, 'refs/tags/') }}
8585
runs-on: ubuntu-latest
8686

87-
defaults:
88-
run:
89-
shell: bash -l {0}
90-
9187
steps:
9288
- uses: actions/checkout@v4
9389

94-
- uses: conda-incubator/setup-miniconda@v3
90+
- uses: actions/setup-python@v5
9591
with:
96-
miniforge-version: latest
97-
environment-file: conda/dev.yaml
98-
channels: conda-forge,nodefaults
99-
activate-environment: pysus
100-
auto-update-conda: true
101-
conda-solver: libmamba
92+
python-version: "3.12"
10293

10394
- name: Install dependencies
10495
run: |
10596
pip install poetry wget
97+
poetry config virtualenvs.create false
10698
poetry install --no-root --with docs
10799
108100
- name: Build docs

README.md

Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -20,13 +20,6 @@ PySUS is a Python package for accessing and analyzing Brazil's public health dat
2020
pip install pysus
2121
```
2222

23-
For DBC file support (requires libffi):
24-
```bash
25-
# Ubuntu/Debian
26-
sudo apt install libffi-dev
27-
pip install pysus[dbc]
28-
```
29-
3023
For the terminal user interface (TUI):
3124
```bash
3225
pip install pysus[tui]

docker/Dockerfile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ COPY docker/scripts/entrypoint.sh /entrypoint.sh
2929
COPY docker/notebooks/ /home/pysus/Notebooks/
3030

3131
RUN pip install poetry \
32-
&& cd /usr/src && poetry config virtualenvs.create false && poetry install --with docs --extras dbc \
32+
&& cd /usr/src && poetry config virtualenvs.create false && poetry install --with docs \
3333
&& pip install 'httpx<0.28' \
3434
&& chown -R pysus:pysus /home/pysus
3535

docs/source/installation.rst

Lines changed: 0 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -14,14 +14,6 @@ The simplest way to install PySUS is via pip:
1414
Extras
1515
^^^^^^
1616

17-
For DBC file support (requires ``libffi``):
18-
19-
.. code-block:: bash
20-
21-
# Ubuntu/Debian
22-
sudo apt install libffi-dev
23-
pip install pysus[dbc]
24-
2517
For the terminal user interface (TUI):
2618

2719
.. code-block:: bash

poetry.lock

Lines changed: 2666 additions & 2246 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

pyproject.toml

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -38,17 +38,15 @@ httpx = ">=0.28.0"
3838
aioftp = "^0.21.4"
3939
dbfread = "2.0.7"
4040
bigtree = "^0.12.2"
41-
42-
pyreaddbc = { version = ">=1.1.0", optional = true }
43-
pycparser = { version = "2.21", optional = true }
44-
textual = { extras = ["syntax"], version = "^8.2.1", optional = true }
45-
humanize = { version = "^4.8.0", optional = true }
41+
pyreaddbc = ">=2.0.4"
4642
dotenv = "^0.9.9"
4743
boto3 = "^1.42.89"
4844
typer = "^0.24.1"
4945

46+
humanize = { version = "^4.8.0", optional = true }
47+
textual = { extras = ["syntax"], version = "^8.2.1", optional = true }
48+
5049
[tool.poetry.extras]
51-
dbc = ["pyreaddbc", "pycparser"]
5250
tui = ["textual", "humanize"]
5351

5452
[tool.poetry.group.dev.dependencies]

pysus/api/client.py

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66

77
import enum
88
from collections.abc import Callable
9-
from datetime import datetime
9+
from datetime import datetime, timezone
1010
from pathlib import Path
1111
from typing import TYPE_CHECKING, Literal
1212

@@ -16,6 +16,7 @@
1616
from pysus import CACHEPATH
1717
from sqlalchemy import DateTime, Enum, Integer, String, create_engine
1818
from sqlalchemy.orm import DeclarativeBase, Mapped, mapped_column, sessionmaker
19+
from sqlalchemy.pool import NullPool
1920

2021
from .dadosgov import DadosGovClient
2122
from .ducklake.client import DuckLake
@@ -61,7 +62,7 @@ class LocalFileState(Base):
6162
sha256: Mapped[str | None] = mapped_column(String, nullable=True)
6263
last_synced: Mapped[datetime] = mapped_column(
6364
DateTime,
64-
default=datetime.utcnow,
65+
default=lambda: datetime.now(timezone.utc).replace(tzinfo=None),
6566
)
6667

6768

@@ -85,7 +86,10 @@ def __init__(self, db_path: Path = CACHEPATH / "config.db"):
8586
db_path.parent.mkdir(parents=True, exist_ok=True)
8687

8788
self.cachepath = db_path.parent
88-
self.engine = create_engine(f"duckdb:///{db_path}")
89+
self.engine = create_engine(
90+
f"duckdb:///{db_path.resolve().as_posix()}",
91+
poolclass=NullPool,
92+
)
8993
Base.metadata.create_all(self.engine)
9094
self.Session = sessionmaker(bind=self.engine)
9195

@@ -239,7 +243,7 @@ async def _update_state(
239243
session.add(record)
240244

241245
record.status = status
242-
record.last_synced = datetime.utcnow()
246+
record.last_synced = datetime.now(timezone.utc).replace(tzinfo=None)
243247
session.commit()
244248

245249
async def download(

pysus/api/extensions.py

Lines changed: 9 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@
22

33
import asyncio
44
import csv
5-
import ctypes.util
65
import gzip
76
import shutil
87
import sys
@@ -26,16 +25,9 @@
2625
from .types import FileType
2726

2827
try:
29-
LIBFFI = True
30-
if sys.platform.startswith("linux"):
31-
LIBFFI = ctypes.util.find_library("ffi") is not None
28+
from pyreaddbc import dbc2dbf
3229

33-
if LIBFFI:
34-
from pyreaddbc import dbc2dbf
35-
36-
DBC_IMPORT = True
37-
else:
38-
DBC_IMPORT = False
30+
DBC_IMPORT = True
3931
except ImportError:
4032
DBC_IMPORT = False
4133

@@ -781,7 +773,7 @@ def _extract():
781773
return list(await asyncio.gather(*tasks))
782774

783775

784-
class FTPNotImported(BaseTabularFile):
776+
class DBCNotImported(BaseTabularFile):
785777
"""Placeholder for DBC files when optional dependency is not installed."""
786778

787779
path: Path = Field(default_factory=lambda: Path("..."))
@@ -872,17 +864,22 @@ class ExtensionFactory:
872864
".csv": CSV,
873865
".parquet": Parquet,
874866
".dbf": DBF,
875-
".dbc": DBC if DBC_IMPORT else FTPNotImported, # type: ignore
867+
".dbc": DBC if DBC_IMPORT else DBCNotImported, # type: ignore
876868
".pdf": PDF,
877869
".json": JSON,
878870
}
879871

872+
_magic_available: bool = sys.platform != "win32"
873+
880874
@classmethod
881875
async def _identify(cls, path: Path) -> type[BaseLocalFile] | None:
882876
"""Identify the file class by its MIME type."""
877+
if not cls._magic_available:
878+
return None
883879
try:
884880
import magic
885881
except (ImportError, OSError):
882+
cls._magic_available = False
886883
return None
887884
try:
888885
mime = await to_thread.run_sync(

pysus/tests/api/ftp/test_models.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -128,15 +128,15 @@ def formatter(self, f):
128128

129129

130130
@pytest.mark.asyncio
131-
async def test_file_download_calls_client(mock_client, mock_dataset):
131+
async def test_file_download_calls_client(mock_client, mock_dataset, tmp_path):
132132
file = File(
133133
path="/root/test.dbc",
134134
_info={"path": "/root/test.dbc", "name": "test.dbc"},
135135
type="file",
136136
dataset=mock_dataset,
137137
)
138138

139-
dest = Path("/tmp/test.dbc")
139+
dest = Path(tmp_path / "test.dbc")
140140
await file._download(output=dest)
141141

142142
mock_client._download_file.assert_called_once_with(file, dest, None)

0 commit comments

Comments
 (0)