quantilica-cli 0.3.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,27 @@
1
+ #!/usr/bin/env bash
2
+ # Formata e verifica os arquivos Python staged com `ruff check --fix` e `ruff format` antes do commit.
3
+ #
4
+ # Instalação (uma vez por clone):
5
+ # git config core.hooksPath .githooks
6
+ #
7
+ set -euo pipefail
8
+
9
+ cd "$(git rev-parse --show-toplevel)"
10
+
11
+ mapfile -t files < <(git diff --cached --name-only --diff-filter=ACMR -- '*.py')
12
+
13
+ if [ "${#files[@]}" -eq 0 ]; then
14
+ exit 0
15
+ fi
16
+
17
+ if ! uv run ruff check --fix "${files[@]}"; then
18
+ echo "pre-commit: ruff check falhou — commit abortado." >&2
19
+ exit 1
20
+ fi
21
+
22
+ if ! uv run ruff format "${files[@]}"; then
23
+ echo "pre-commit: ruff format falhou — commit abortado." >&2
24
+ exit 1
25
+ fi
26
+
27
+ git add -- "${files[@]}"
@@ -0,0 +1,66 @@
1
+ name: Publish to PyPI
2
+
3
+ on:
4
+ push:
5
+ tags:
6
+ - "v*"
7
+
8
+ jobs:
9
+ build:
10
+ name: Build distribution
11
+ runs-on: ubuntu-latest
12
+ steps:
13
+ - uses: actions/checkout@v4
14
+
15
+ - name: Install uv
16
+ uses: astral-sh/setup-uv@v5
17
+
18
+ - name: Build sdist and wheel
19
+ run: uv build
20
+
21
+ - name: Upload build artifacts
22
+ uses: actions/upload-artifact@v4
23
+ with:
24
+ name: dist
25
+ path: dist/
26
+
27
+ publish-testpypi:
28
+ name: Publish to TestPyPI
29
+ needs: build
30
+ runs-on: ubuntu-latest
31
+ environment:
32
+ name: testpypi
33
+ url: https://test.pypi.org/p/quantilica-cli
34
+ permissions:
35
+ id-token: write
36
+ steps:
37
+ - name: Download build artifacts
38
+ uses: actions/download-artifact@v4
39
+ with:
40
+ name: dist
41
+ path: dist/
42
+
43
+ - name: Publish to TestPyPI
44
+ uses: pypa/gh-action-pypi-publish@release/v1
45
+ with:
46
+ repository-url: https://test.pypi.org/legacy/
47
+ skip-existing: true
48
+
49
+ publish-pypi:
50
+ name: Publish to PyPI
51
+ needs: publish-testpypi
52
+ runs-on: ubuntu-latest
53
+ environment:
54
+ name: pypi
55
+ url: https://pypi.org/p/quantilica-cli
56
+ permissions:
57
+ id-token: write
58
+ steps:
59
+ - name: Download build artifacts
60
+ uses: actions/download-artifact@v4
61
+ with:
62
+ name: dist
63
+ path: dist/
64
+
65
+ - name: Publish to PyPI
66
+ uses: pypa/gh-action-pypi-publish@release/v1
@@ -0,0 +1,38 @@
1
+ name: Test
2
+
3
+ on:
4
+ push:
5
+ branches: [main]
6
+ pull_request:
7
+ branches: [main]
8
+
9
+ jobs:
10
+ test:
11
+ name: Test (Python ${{ matrix.python-version }})
12
+ runs-on: ubuntu-latest
13
+ strategy:
14
+ fail-fast: false
15
+ matrix:
16
+ python-version: ["3.12", "3.13"]
17
+
18
+ steps:
19
+ - uses: actions/checkout@v4
20
+
21
+ - name: Install uv
22
+ uses: astral-sh/setup-uv@v5
23
+ with:
24
+ enable-cache: true
25
+
26
+ - name: Set up Python ${{ matrix.python-version }}
27
+ run: uv python install ${{ matrix.python-version }}
28
+
29
+ - name: Install dependencies
30
+ run: uv sync --group dev
31
+
32
+ - name: Lint with ruff
33
+ run: |
34
+ uv run ruff check src/ tests/
35
+ uv run ruff format --check src/ tests/
36
+
37
+ - name: Run tests
38
+ run: uv run pytest
@@ -0,0 +1,38 @@
1
+ # Build / packaging
2
+ __pycache__/
3
+ *.py[cod]
4
+ *.egg-info/
5
+ build/
6
+ dist/
7
+
8
+ # Virtual envs e caches do uv
9
+ .venv/
10
+ .uv-cache/
11
+
12
+ # Lockfile — libs não versionam uv.lock
13
+ uv.lock
14
+
15
+ # Caches de teste / lint / tipos
16
+ .pytest_cache/
17
+ .ruff_cache/
18
+ .mypy_cache/
19
+
20
+ # Cobertura
21
+ .coverage
22
+ .coverage.*
23
+ htmlcov/
24
+
25
+ # Editor / IDE / OS
26
+ .vscode/
27
+ .idea/
28
+ .DS_Store
29
+ Thumbs.db
30
+ *.swp
31
+
32
+ # Logs e env locais
33
+ *.log
34
+ .env
35
+ .env.local
36
+
37
+ # Claude
38
+ .claude/
@@ -0,0 +1,35 @@
1
+ # Changelog
2
+
3
+ Todas as mudanças notáveis deste projeto serão documentadas neste arquivo.
4
+
5
+ O formato segue [Keep a Changelog](https://keepachangelog.com/pt-BR/1.1.0/),
6
+ e este projeto adere ao [Semantic Versioning](https://semver.org/lang/pt-BR/).
7
+
8
+ ## [0.3.0] - 2026-08-10
9
+
10
+ ### Adicionado
11
+ - Extensão da `FetcherApp` (`sdk.py`) com suporte opcional para `FtpClient`.
12
+ - Exposição do método `download_datasets` na `FetcherApp` para uso por comandos Typer customizados nos plugins.
13
+ - Aceitação e passagem do parâmetro `aliases_dict` para personalização total dos subcomandos por fetcher.
14
+
15
+ ### Alterado
16
+ - Padrão de metadados do pacote portado integralmente para a PEP 639 (licença) e PEP 561 (tipagem estática).
17
+
18
+ ## [0.2.2] - 2026-07-28
19
+ *(Release retroativo não documentado)*
20
+
21
+ ## [0.2.0] - 2026-07-15
22
+ *(Release retroativo não documentado)*
23
+
24
+ ## [0.1.0] - 2026-06-04
25
+
26
+ Primeira entrada em formato Keep a Changelog; documenta o estado do pacote nesta
27
+ versão.
28
+
29
+ ### Adicionado
30
+
31
+ - CLI unificada `quantilica` que descobre e monta os fetchers instalados via
32
+ entry points `quantilica.fetchers`, sem depender diretamente dos pacotes de
33
+ fetcher.
34
+ - Comando `list-sources` e montagem automática dos sub-apps Typer de cada fetcher
35
+ instalado (`quantilica <fonte> ...`).
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Quantilica
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,48 @@
1
+ Metadata-Version: 2.4
2
+ Name: quantilica-cli
3
+ Version: 0.3.0
4
+ Summary: Unified CLI for Quantilica open data fetchers
5
+ Author-email: "Komesu, D.K." <daniel@dkko.me>
6
+ License-Expression: MIT
7
+ License-File: LICENSE
8
+ Keywords: brazil,cli,open-data,quantilica
9
+ Classifier: Development Status :: 3 - Alpha
10
+ Classifier: Environment :: Console
11
+ Classifier: Intended Audience :: Developers
12
+ Classifier: Intended Audience :: Science/Research
13
+ Classifier: Operating System :: OS Independent
14
+ Classifier: Programming Language :: Python :: 3
15
+ Classifier: Programming Language :: Python :: 3.12
16
+ Classifier: Programming Language :: Python :: 3.13
17
+ Classifier: Typing :: Typed
18
+ Requires-Python: >=3.12
19
+ Requires-Dist: quantilica-core>=0.4.0
20
+ Requires-Dist: rich>=13.0.0
21
+ Requires-Dist: typer>=0.15.0
22
+ Description-Content-Type: text/markdown
23
+
24
+ # quantilica-cli
25
+
26
+ CLI unificada para o ecossistema Quantilica de dados abertos brasileiros.
27
+
28
+ ## Instalação
29
+
30
+ ```bash
31
+ pip install quantilica-cli
32
+ ```
33
+
34
+ Instale os fetchers desejados separadamente:
35
+
36
+ ```bash
37
+ pip install comex-fetcher inmet-fetcher rtn-fetcher
38
+ ```
39
+
40
+ ## Uso
41
+
42
+ ```bash
43
+ quantilica --help
44
+ quantilica list-sources
45
+ quantilica comex sync 2024
46
+ quantilica inmet sync 2020 2021
47
+ quantilica td sync --dataset prices
48
+ ```
@@ -0,0 +1,25 @@
1
+ # quantilica-cli
2
+
3
+ CLI unificada para o ecossistema Quantilica de dados abertos brasileiros.
4
+
5
+ ## Instalação
6
+
7
+ ```bash
8
+ pip install quantilica-cli
9
+ ```
10
+
11
+ Instale os fetchers desejados separadamente:
12
+
13
+ ```bash
14
+ pip install comex-fetcher inmet-fetcher rtn-fetcher
15
+ ```
16
+
17
+ ## Uso
18
+
19
+ ```bash
20
+ quantilica --help
21
+ quantilica list-sources
22
+ quantilica comex sync 2024
23
+ quantilica inmet sync 2020 2021
24
+ quantilica td sync --dataset prices
25
+ ```
@@ -0,0 +1,70 @@
1
+ [project]
2
+ name = "quantilica-cli"
3
+ version = "0.3.0"
4
+ description = "Unified CLI for Quantilica open data fetchers"
5
+ readme = "README.md"
6
+ authors = [{ name = "Komesu, D.K.", email = "daniel@dkko.me" }]
7
+ license = "MIT"
8
+ license-files = ["LICENSE"]
9
+ requires-python = ">=3.12"
10
+ keywords = ["quantilica", "cli", "open-data", "brazil"]
11
+ classifiers = [
12
+ "Development Status :: 3 - Alpha",
13
+ "Environment :: Console",
14
+ "Intended Audience :: Science/Research",
15
+ "Intended Audience :: Developers",
16
+ "Typing :: Typed",
17
+ "Operating System :: OS Independent",
18
+ "Programming Language :: Python :: 3",
19
+ "Programming Language :: Python :: 3.12",
20
+ "Programming Language :: Python :: 3.13",
21
+ ]
22
+ dependencies = [
23
+ "quantilica-core>=0.4.0",
24
+ "rich>=13.0.0",
25
+ "typer>=0.15.0",
26
+ ]
27
+
28
+ [project.scripts]
29
+ quantilica = "quantilica.cli.cli:main"
30
+
31
+ [dependency-groups]
32
+ dev = ["ruff>=0.9.0", "pytest>=8.0.0"]
33
+
34
+ # Os fetchers são instalados independentemente pelo usuário — quantilica-cli
35
+ # os descobre via entry points (grupo "quantilica.fetchers") sem dependência direta.
36
+ # Exemplo de instalação local para desenvolvimento:
37
+ # uv pip install -e ../comex-fetcher ../rtn-fetcher ...
38
+
39
+ # Para desenvolvimento local do quantilica-core em conjunto com o CLI:
40
+ # cd quantilica-cli && uv pip install -e ../quantilica-core (sem [tool.uv.sources])
41
+ # Em produção, quantilica-core é instalado via git+https (declarado em dependencies).
42
+
43
+ # [tool.hatch.metadata]
44
+ # allow-direct-references = true
45
+
46
+ [tool.hatch.build]
47
+ sources = ["src"]
48
+
49
+ [tool.hatch.build.targets.wheel]
50
+ packages = ["src/quantilica/cli"]
51
+
52
+ [build-system]
53
+ requires = ["hatchling>=1.27"]
54
+ build-backend = "hatchling.build"
55
+
56
+ [tool.pytest.ini_options]
57
+ testpaths = ["tests"]
58
+ addopts = ["-p", "no:cacheprovider"]
59
+
60
+ [tool.ruff]
61
+ line-length = 88
62
+
63
+ [tool.ruff.lint]
64
+ select = ["E", "F", "I", "UP", "B"]
65
+
66
+ [tool.ruff.lint.per-file-ignores]
67
+ "src/quantilica/cli/sources.py" = ["E501"]
68
+ "src/quantilica/cli/cli.py" = ["E501"]
69
+ "src/quantilica/cli/sdk.py" = ["E501"]
70
+ "tests/*" = ["E501"]
@@ -0,0 +1,10 @@
1
+ """Unified CLI for Quantilica open data fetchers."""
2
+
3
+ from importlib.metadata import PackageNotFoundError, version
4
+
5
+ try:
6
+ __version__ = version("quantilica-cli")
7
+ except PackageNotFoundError:
8
+ __version__ = "0.0.0"
9
+
10
+ __all__ = ["__version__"]
@@ -0,0 +1,154 @@
1
+ """Unified CLI entry point for Quantilica fetchers."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import logging
6
+ import sys
7
+ from importlib.metadata import entry_points
8
+ from typing import Annotated
9
+
10
+ import typer
11
+ from rich.console import Console
12
+ from rich.logging import RichHandler
13
+ from rich.table import Table
14
+
15
+ from quantilica.cli import __version__
16
+ from quantilica.cli.manifests import app as manifests_app
17
+ from quantilica.cli.sources import (
18
+ app as sources_app,
19
+ )
20
+ from quantilica.cli.sources import (
21
+ cmd_doctor,
22
+ cmd_install,
23
+ cmd_uninstall,
24
+ fetch_remote_sources,
25
+ )
26
+
27
+ FETCHER_GROUP = "quantilica.fetchers"
28
+ COMMAND_GROUP = "quantilica.commands"
29
+
30
+ app = typer.Typer(
31
+ name="quantilica",
32
+ help="Quantilica — ferramentas de dados abertos brasileiros.",
33
+ no_args_is_help=True,
34
+ )
35
+ app.add_typer(manifests_app, name="manifests")
36
+ app.add_typer(sources_app, name="sources")
37
+
38
+ # Adiciona comandos top-level install, uninstall e doctor
39
+ app.command("install")(cmd_install)
40
+ app.command("uninstall")(cmd_uninstall)
41
+ app.command("doctor")(cmd_doctor)
42
+
43
+ console = Console()
44
+
45
+
46
+ def _load_plugins(group: str) -> dict[str, typer.Typer]:
47
+ plugins: dict[str, typer.Typer] = {}
48
+ for ep in entry_points(group=group):
49
+ try:
50
+ plugins[ep.name] = ep.load()
51
+ except Exception as exc:
52
+ console.print(
53
+ f"[yellow]Aviso:[/yellow] falha ao carregar plugin '{ep.name}': {exc}"
54
+ )
55
+ return plugins
56
+
57
+
58
+ def _register_plugins() -> None:
59
+ # Fetchers e comandos ficam na raiz: `quantilica <nome>`.
60
+ for name, plugin_app in _load_plugins(FETCHER_GROUP).items():
61
+ app.add_typer(plugin_app, name=name)
62
+ for name, plugin_app in _load_plugins(COMMAND_GROUP).items():
63
+ app.add_typer(plugin_app, name=name)
64
+
65
+
66
+ _register_plugins()
67
+
68
+
69
+ def _version_callback(value: bool) -> None:
70
+ if value:
71
+ console.print(f"quantilica {__version__}")
72
+ raise typer.Exit()
73
+
74
+
75
+ @app.callback()
76
+ def root_callback(
77
+ version: bool | None = typer.Option(
78
+ None,
79
+ "--version",
80
+ "-V",
81
+ callback=_version_callback,
82
+ is_eager=True,
83
+ help="Exibe a versão e encerra.",
84
+ ),
85
+ ) -> None:
86
+ """Quantilica CLI."""
87
+
88
+
89
+ @app.command("list-sources")
90
+ def list_sources(
91
+ remote: Annotated[
92
+ bool,
93
+ typer.Option(
94
+ "--remote", help="Exibe todas as fontes conhecidas no registro remoto"
95
+ ),
96
+ ] = False,
97
+ ) -> None:
98
+ """Lista as fontes de dados instaladas ou disponíveis no repositório."""
99
+ plugins = _load_plugins(FETCHER_GROUP)
100
+
101
+ if remote:
102
+ remote_registry = fetch_remote_sources()
103
+ table = Table(title="Todas as fontes de dados conhecidas", show_header=True)
104
+ table.add_column("Fonte", style="cyan")
105
+ table.add_column("Pacote", style="magenta")
106
+ table.add_column("Status", style="green")
107
+
108
+ for name, dist in sorted(remote_registry.items()):
109
+ status = (
110
+ "[green]Instalado[/green]"
111
+ if name in plugins
112
+ else "[dim]Não instalado[/dim]"
113
+ )
114
+ table.add_row(name, dist, status)
115
+
116
+ console.print(table)
117
+ return
118
+
119
+ if not plugins:
120
+ console.print(
121
+ "[yellow]Nenhum fetcher instalado.[/yellow]\n"
122
+ "Use 'quantilica install <fonte>' para instalar um fetcher (ex: quantilica install comex)."
123
+ )
124
+ return
125
+
126
+ table = Table(title="Fontes instaladas", show_header=True)
127
+ table.add_column("Comando", style="cyan")
128
+ table.add_column("Descrição", style="green")
129
+
130
+ for name, plugin_app in sorted(plugins.items()):
131
+ info = getattr(plugin_app, "info", None)
132
+ description = ""
133
+ if info and hasattr(info, "help") and info.help:
134
+ description = info.help
135
+ table.add_row(f"quantilica {name}", description)
136
+
137
+ console.print(table)
138
+
139
+
140
+ def main() -> None:
141
+ if hasattr(sys.stdout, "reconfigure"):
142
+ sys.stdout.reconfigure(encoding="utf-8", errors="replace") # type: ignore
143
+ logging.basicConfig(
144
+ level=logging.INFO,
145
+ format="%(message)s",
146
+ datefmt="[%X]",
147
+ handlers=[RichHandler(rich_tracebacks=True, show_path=False)],
148
+ force=True,
149
+ )
150
+ app()
151
+
152
+
153
+ if __name__ == "__main__":
154
+ main()