pinky-core 0.1.0.dev4__tar.gz → 0.1.0.dev6__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.
- {pinky_core-0.1.0.dev4 → pinky_core-0.1.0.dev6}/.github/workflows/ci.yml +12 -6
- {pinky_core-0.1.0.dev4 → pinky_core-0.1.0.dev6}/.gitignore +4 -1
- {pinky_core-0.1.0.dev4 → pinky_core-0.1.0.dev6}/.pre-commit-config.yaml +47 -4
- pinky_core-0.1.0.dev6/.python-version +1 -0
- pinky_core-0.1.0.dev6/.vscode/settings.json +8 -0
- pinky_core-0.1.0.dev6/CONTEXT.md +24 -0
- {pinky_core-0.1.0.dev4 → pinky_core-0.1.0.dev6}/PKG-INFO +17 -16
- {pinky_core-0.1.0.dev4 → pinky_core-0.1.0.dev6}/README.md +16 -2
- pinky_core-0.1.0.dev6/docs/adr/0001-zero-dependency-static-html.md +26 -0
- {pinky_core-0.1.0.dev4 → pinky_core-0.1.0.dev6}/docs/explanation/2-features.md +2 -2
- {pinky_core-0.1.0.dev4 → pinky_core-0.1.0.dev6}/docs/reference/generated/coverage_badge.svg +1 -1
- {pinky_core-0.1.0.dev4 → pinky_core-0.1.0.dev6}/docs/reference/generated/hooks_badge.svg +3 -3
- {pinky_core-0.1.0.dev4 → pinky_core-0.1.0.dev6}/pyproject.toml +21 -2
- pinky_core-0.1.0.dev6/scripts/hooks/check_weak_assertions.py +251 -0
- {pinky_core-0.1.0.dev4 → pinky_core-0.1.0.dev6}/scripts/hooks/commit-msg +16 -17
- pinky_core-0.1.0.dev6/scripts/hooks/generate-docs +108 -0
- pinky_core-0.1.0.dev6/scripts/hooks/run-tests +67 -0
- pinky_core-0.1.0.dev6/src/pinky_core/__init__.py +132 -0
- {pinky_core-0.1.0.dev4 → pinky_core-0.1.0.dev6}/src/pinky_core/files.py +1 -1
- {pinky_core-0.1.0.dev4 → pinky_core-0.1.0.dev6}/src/pinky_core/html.py +9 -9
- pinky_core-0.1.0.dev6/tests/fixtures/golden/dl_card_basic.html +29 -0
- pinky_core-0.1.0.dev6/tests/fixtures/golden/dl_card_expiry.html +29 -0
- pinky_core-0.1.0.dev6/tests/fixtures/golden/generate_html_basic.html +67 -0
- pinky_core-0.1.0.dev6/tests/fixtures/golden/task_table_error.html +20 -0
- pinky_core-0.1.0.dev6/tests/fixtures/golden/task_table_no_snowsight.html +20 -0
- pinky_core-0.1.0.dev6/tests/fixtures/golden/task_table_snowsight.html +20 -0
- pinky_core-0.1.0.dev6/tests/test_html.py +102 -0
- pinky_core-0.1.0.dev6/uv.lock +1826 -0
- pinky_core-0.1.0.dev4/docs/adr/0001-zero-dependency-static-html.md +0 -26
- pinky_core-0.1.0.dev4/scripts/hooks/generate-docs +0 -65
- pinky_core-0.1.0.dev4/src/pinky_core/__init__.py +0 -221
- pinky_core-0.1.0.dev4/tests/test_html.py +0 -63
- {pinky_core-0.1.0.dev4 → pinky_core-0.1.0.dev6}/.github/workflows/publish.yml +0 -0
- {pinky_core-0.1.0.dev4 → pinky_core-0.1.0.dev6}/.github/workflows/release.yml +0 -0
- {pinky_core-0.1.0.dev4 → pinky_core-0.1.0.dev6}/.gitleaks.toml +0 -0
- {pinky_core-0.1.0.dev4 → pinky_core-0.1.0.dev6}/.secrets.baseline +0 -0
- {pinky_core-0.1.0.dev4 → pinky_core-0.1.0.dev6}/LICENSE +0 -0
- {pinky_core-0.1.0.dev4 → pinky_core-0.1.0.dev6}/docs/.pages +0 -0
- {pinky_core-0.1.0.dev4 → pinky_core-0.1.0.dev6}/docs/adr/.pages +0 -0
- {pinky_core-0.1.0.dev4 → pinky_core-0.1.0.dev6}/docs/explanation/.pages +0 -0
- {pinky_core-0.1.0.dev4 → pinky_core-0.1.0.dev6}/docs/explanation/1-design.md +0 -0
- {pinky_core-0.1.0.dev4 → pinky_core-0.1.0.dev6}/docs/how-to/.pages +0 -0
- {pinky_core-0.1.0.dev4 → pinky_core-0.1.0.dev6}/docs/how-to/index.md +0 -0
- {pinky_core-0.1.0.dev4 → pinky_core-0.1.0.dev6}/docs/index.md +0 -0
- {pinky_core-0.1.0.dev4 → pinky_core-0.1.0.dev6}/docs/mkdocs.yml +0 -0
- {pinky_core-0.1.0.dev4 → pinky_core-0.1.0.dev6}/docs/reference/.pages +0 -0
- {pinky_core-0.1.0.dev4 → pinky_core-0.1.0.dev6}/docs/reference/api/.pages +0 -0
- {pinky_core-0.1.0.dev4 → pinky_core-0.1.0.dev6}/docs/reference/api/fec.md +0 -0
- {pinky_core-0.1.0.dev4 → pinky_core-0.1.0.dev6}/docs/reference/api/files.md +0 -0
- {pinky_core-0.1.0.dev4 → pinky_core-0.1.0.dev6}/docs/reference/api/fmt.md +0 -0
- {pinky_core-0.1.0.dev4 → pinky_core-0.1.0.dev6}/docs/reference/api/html.md +0 -0
- {pinky_core-0.1.0.dev4 → pinky_core-0.1.0.dev6}/docs/reference/api/lint.md +0 -0
- {pinky_core-0.1.0.dev4 → pinky_core-0.1.0.dev6}/docs/reference/api/schedule.md +0 -0
- {pinky_core-0.1.0.dev4 → pinky_core-0.1.0.dev6}/docs/reference/api/security.md +0 -0
- {pinky_core-0.1.0.dev4 → pinky_core-0.1.0.dev6}/docs/reference/api/sql.md +0 -0
- {pinky_core-0.1.0.dev4 → pinky_core-0.1.0.dev6}/docs/reference/api/validate.md +0 -0
- {pinky_core-0.1.0.dev4 → pinky_core-0.1.0.dev6}/docs/reference/api/warnings.md +0 -0
- {pinky_core-0.1.0.dev4 → pinky_core-0.1.0.dev6}/docs/reference/api/xml.md +0 -0
- {pinky_core-0.1.0.dev4 → pinky_core-0.1.0.dev6}/docs/reference/generated/interrogate_badge.svg +0 -0
- {pinky_core-0.1.0.dev4 → pinky_core-0.1.0.dev6}/docs/reference/generated/noqa_badge.svg +0 -0
- {pinky_core-0.1.0.dev4 → pinky_core-0.1.0.dev6}/docs/tutorials/.pages +0 -0
- {pinky_core-0.1.0.dev4 → pinky_core-0.1.0.dev6}/docs/tutorials/index.md +0 -0
- {pinky_core-0.1.0.dev4 → pinky_core-0.1.0.dev6}/scripts/deploy_validate_udfs.py +0 -0
- {pinky_core-0.1.0.dev4 → pinky_core-0.1.0.dev6}/scripts/hooks/badges.toml +0 -0
- {pinky_core-0.1.0.dev4 → pinky_core-0.1.0.dev6}/scripts/hooks/check_forbidden_keywords.py +0 -0
- {pinky_core-0.1.0.dev4 → pinky_core-0.1.0.dev6}/scripts/hooks/check_snowpark_api.py +0 -0
- {pinky_core-0.1.0.dev4 → pinky_core-0.1.0.dev6}/scripts/hooks/check_typed_api.py +0 -0
- {pinky_core-0.1.0.dev4 → pinky_core-0.1.0.dev6}/scripts/hooks/check_zws.py +0 -0
- {pinky_core-0.1.0.dev4 → pinky_core-0.1.0.dev6}/scripts/hooks/docs_identity.json +0 -0
- {pinky_core-0.1.0.dev4 → pinky_core-0.1.0.dev6}/scripts/hooks/generate_ci_badges.py +0 -0
- {pinky_core-0.1.0.dev4 → pinky_core-0.1.0.dev6}/scripts/hooks/render_docs.py +0 -0
- {pinky_core-0.1.0.dev4 → pinky_core-0.1.0.dev6}/sql/deploy_udf_example.sql +0 -0
- {pinky_core-0.1.0.dev4 → pinky_core-0.1.0.dev6}/src/pinky_core/finance/__init__.py +0 -0
- {pinky_core-0.1.0.dev4 → pinky_core-0.1.0.dev6}/src/pinky_core/finance/fec.py +0 -0
- {pinky_core-0.1.0.dev4 → pinky_core-0.1.0.dev6}/src/pinky_core/fmt.py +0 -0
- {pinky_core-0.1.0.dev4 → pinky_core-0.1.0.dev6}/src/pinky_core/lint.py +0 -0
- {pinky_core-0.1.0.dev4 → pinky_core-0.1.0.dev6}/src/pinky_core/schedule.py +0 -0
- {pinky_core-0.1.0.dev4 → pinky_core-0.1.0.dev6}/src/pinky_core/security.py +0 -0
- {pinky_core-0.1.0.dev4 → pinky_core-0.1.0.dev6}/src/pinky_core/sql.py +0 -0
- {pinky_core-0.1.0.dev4 → pinky_core-0.1.0.dev6}/src/pinky_core/validate.py +0 -0
- {pinky_core-0.1.0.dev4 → pinky_core-0.1.0.dev6}/src/pinky_core/warnings.py +0 -0
- {pinky_core-0.1.0.dev4 → pinky_core-0.1.0.dev6}/src/pinky_core/xml.py +0 -0
- {pinky_core-0.1.0.dev4 → pinky_core-0.1.0.dev6}/tests/__init__.py +0 -0
- {pinky_core-0.1.0.dev4 → pinky_core-0.1.0.dev6}/tests/fixtures/cats_domination.xml +0 -0
- {pinky_core-0.1.0.dev4 → pinky_core-0.1.0.dev6}/tests/test_fec.py +0 -0
- {pinky_core-0.1.0.dev4 → pinky_core-0.1.0.dev6}/tests/test_files.py +0 -0
- {pinky_core-0.1.0.dev4 → pinky_core-0.1.0.dev6}/tests/test_fmt.py +0 -0
- {pinky_core-0.1.0.dev4 → pinky_core-0.1.0.dev6}/tests/test_lint.py +0 -0
- {pinky_core-0.1.0.dev4 → pinky_core-0.1.0.dev6}/tests/test_public_api.py +0 -0
- {pinky_core-0.1.0.dev4 → pinky_core-0.1.0.dev6}/tests/test_schedule.py +0 -0
- {pinky_core-0.1.0.dev4 → pinky_core-0.1.0.dev6}/tests/test_security.py +0 -0
- {pinky_core-0.1.0.dev4 → pinky_core-0.1.0.dev6}/tests/test_sql.py +0 -0
- {pinky_core-0.1.0.dev4 → pinky_core-0.1.0.dev6}/tests/test_validate.py +0 -0
- {pinky_core-0.1.0.dev4 → pinky_core-0.1.0.dev6}/tests/test_warnings.py +0 -0
- {pinky_core-0.1.0.dev4 → pinky_core-0.1.0.dev6}/tests/test_xml_xpath.py +0 -0
|
@@ -3,16 +3,20 @@ name: CI
|
|
|
3
3
|
on:
|
|
4
4
|
push:
|
|
5
5
|
branches: [main, develop]
|
|
6
|
-
paths: ['src/**', 'tests/**', 'pyproject.toml', 'scripts/**', '.pre-commit-config.yaml', '.github/workflows/ci.yml']
|
|
7
6
|
pull_request:
|
|
8
7
|
branches: [main, develop]
|
|
9
|
-
paths: ['src/**', 'tests/**', 'pyproject.toml', 'scripts/**', '.pre-commit-config.yaml', '.github/workflows/ci.yml']
|
|
10
8
|
|
|
11
9
|
jobs:
|
|
12
10
|
ci:
|
|
13
11
|
runs-on: ubuntu-latest
|
|
14
12
|
env:
|
|
15
13
|
FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: true
|
|
14
|
+
# Explicite, alors que le défaut d'org est déjà `read` : ce défaut est un réglage
|
|
15
|
+
# d'organisation, invisible du fichier et modifiable sans toucher aucun repo. Le déclarer ici
|
|
16
|
+
# rend le workflow immunisé si ce défaut repasse un jour en `write` (règle CodeQL
|
|
17
|
+
# actions/missing-workflow-permissions, 14 alertes sur l'org au scan 2026-07-25).
|
|
18
|
+
permissions:
|
|
19
|
+
contents: read
|
|
16
20
|
|
|
17
21
|
steps:
|
|
18
22
|
- uses: actions/checkout@v4
|
|
@@ -33,10 +37,10 @@ jobs:
|
|
|
33
37
|
uses: j178/prek-action@v2
|
|
34
38
|
|
|
35
39
|
- name: Install dependencies
|
|
36
|
-
#
|
|
37
|
-
#
|
|
38
|
-
#
|
|
39
|
-
run: pip install -e ".[validate,address,schedule,xml,lint
|
|
40
|
+
# --group dev, pas ".[dev]" : les deps de dev sont un dependency-group PEP 735 depuis
|
|
41
|
+
# 2026-07-22 (voir le commentaire dans pyproject.toml — `uv run` désinstallait les extras).
|
|
42
|
+
# Requiert pip >= 25.1 ; ensurepip en livre 26.1.2 avec Python 3.12, donc rien à épingler.
|
|
43
|
+
run: pip install -e ".[validate,address,schedule,xml,lint]" --group dev
|
|
40
44
|
|
|
41
45
|
- name: Tests
|
|
42
46
|
run: pytest
|
|
@@ -45,6 +49,8 @@ jobs:
|
|
|
45
49
|
# ADR-0023 pilot gate: hermetic pre-commit hooks must run on the brother's
|
|
46
50
|
# machine (Windows, prek only, no toolchain) — this is the real gate, not the Mac.
|
|
47
51
|
runs-on: windows-latest
|
|
52
|
+
permissions:
|
|
53
|
+
contents: read
|
|
48
54
|
strategy:
|
|
49
55
|
matrix:
|
|
50
56
|
shell: [cmd, pwsh]
|
|
@@ -12,7 +12,10 @@ build/
|
|
|
12
12
|
|
|
13
13
|
# ── Environments ─────────────────────────────────────────────────────────────
|
|
14
14
|
.env
|
|
15
|
-
|
|
15
|
+
# No trailing slash: with `centralized-project-envs`, uv puts the env in ~/.cache/uv and drops a
|
|
16
|
+
# SYMLINK named .venv here. `.venv/` matches directories only, so the link stayed untracked-visible
|
|
17
|
+
# and committable — a machine-absolute path, useless on any other machine.
|
|
18
|
+
.venv
|
|
16
19
|
venv/
|
|
17
20
|
.conda/
|
|
18
21
|
|
|
@@ -15,6 +15,15 @@ default_install_hook_types: [pre-commit, commit-msg]
|
|
|
15
15
|
# Pin explicite : sans ça, les hooks `language: python` (mdformat, mypy, interrogate, darglint…)
|
|
16
16
|
# créent leur venv avec le python3 ambiant du PATH au moment de l'install, pas une version garantie
|
|
17
17
|
# par le repo — non-déterministe en local (CI est protégée par actions/setup-python en amont).
|
|
18
|
+
#
|
|
19
|
+
# Règle (2026-07-22) : tout hook `repo: local` dont l'`entry` commence par `python` DOIT être
|
|
20
|
+
# `language: python`, jamais `language: system`. `system` exécute la commande telle quelle dans le
|
|
21
|
+
# shell, et `python` nu n'existe ni sur macOS hors conda (homebrew ne pose que `python3`) ni sur une
|
|
22
|
+
# machine Windows vanilla — ça ne marchait que par l'auto-activation de conda base. Avec
|
|
23
|
+
# `language: python`, prek provisionne l'interpréteur (et le télécharge s'il manque) et met le bin/
|
|
24
|
+
# de son venv en tête du PATH : `python` y est garanti sur les trois OS. Les scripts de
|
|
25
|
+
# scripts/hooks/ étant 100 % stdlib, ils partagent tous un seul venv (dédupliqué par langage +
|
|
26
|
+
# version + additional_dependencies) — coût quasi nul.
|
|
18
27
|
default_language_version:
|
|
19
28
|
python: python3.12
|
|
20
29
|
|
|
@@ -43,6 +52,28 @@ repos:
|
|
|
43
52
|
stages: [pre-commit]
|
|
44
53
|
pass_filenames: false
|
|
45
54
|
|
|
55
|
+
# ── Test suite: the blocking quality gate ─────────────────────────────────
|
|
56
|
+
# Universal, NOT the `python` extra (moved 2026-07-25, same day it was introduced). The first
|
|
57
|
+
# placement rested on an unverified assumption — "the repos outside the python extra have no test
|
|
58
|
+
# suite". Measured: pinky-workspace, the engine itself, carries 25 test files / 264 tests, and
|
|
59
|
+
# pinky-studio carries tests with NO CI at all. Only assets/infra have no tests/ directory, and
|
|
60
|
+
# the hook's own `[ -d tests ]` guard makes those a real no-op.
|
|
61
|
+
#
|
|
62
|
+
# Before this hook, the only test execution at commit time was the one buried inside generate-docs
|
|
63
|
+
# feeding the coverage badge, whose failure printed a warning and returned 0 — a broken suite
|
|
64
|
+
# passed pre-commit. generate-docs keeps its own run for the badge data and stays non-blocking;
|
|
65
|
+
# this hook owns the verdict. See core/hooks/run-tests for why the two runs stay independent, and
|
|
66
|
+
# why an absent pytest warns instead of blocking.
|
|
67
|
+
- repo: local
|
|
68
|
+
hooks:
|
|
69
|
+
- id: run-tests
|
|
70
|
+
name: "[TESTS] Suite must pass"
|
|
71
|
+
entry: scripts/hooks/run-tests
|
|
72
|
+
language: script
|
|
73
|
+
files: ^(src|tests)/
|
|
74
|
+
pass_filenames: false
|
|
75
|
+
stages: [pre-commit]
|
|
76
|
+
|
|
46
77
|
# ── Formatting: Markdown ──────────────────────────────────────────────────
|
|
47
78
|
- repo: local
|
|
48
79
|
hooks:
|
|
@@ -80,7 +111,7 @@ repos:
|
|
|
80
111
|
- id: check-zws
|
|
81
112
|
name: "[ZWS] Invisible characters (zero-width)"
|
|
82
113
|
entry: python scripts/hooks/check_zws.py
|
|
83
|
-
language:
|
|
114
|
+
language: python
|
|
84
115
|
pass_filenames: true
|
|
85
116
|
|
|
86
117
|
# ── Employer reference guard ──────────────────────────────────────────────
|
|
@@ -89,7 +120,7 @@ repos:
|
|
|
89
120
|
- id: check-forbidden-keywords
|
|
90
121
|
name: "[FORBIDDEN] Employer references"
|
|
91
122
|
entry: python scripts/hooks/check_forbidden_keywords.py
|
|
92
|
-
language:
|
|
123
|
+
language: python
|
|
93
124
|
pass_filenames: true
|
|
94
125
|
|
|
95
126
|
# ── Commit message format ─────────────────────────────────────────────────
|
|
@@ -171,10 +202,22 @@ repos:
|
|
|
171
202
|
- id: check-typed-api
|
|
172
203
|
name: "[TYPED-API] No raw dict/tuple/set in public annotations"
|
|
173
204
|
entry: python scripts/hooks/check_typed_api.py
|
|
174
|
-
language:
|
|
205
|
+
language: python
|
|
175
206
|
types: [python]
|
|
176
207
|
files: ^src/
|
|
177
208
|
|
|
209
|
+
# Weak-assertion ratchet — first hook to target tests/, not src/ (tests.md § generator oracle).
|
|
210
|
+
# Blocks a fragment `assert "x" in <generator(...)>` that has no whole-output equality oracle in
|
|
211
|
+
# the same file. Narrow by design (name-matched generators only) so it stays at ~0 flags.
|
|
212
|
+
- repo: local
|
|
213
|
+
hooks:
|
|
214
|
+
- id: check-weak-assertions
|
|
215
|
+
name: "[WEAK-ASSERT] Generated-output assertion needs an equality oracle"
|
|
216
|
+
entry: python scripts/hooks/check_weak_assertions.py
|
|
217
|
+
language: python
|
|
218
|
+
types: [python]
|
|
219
|
+
files: ^tests/
|
|
220
|
+
|
|
178
221
|
# ── EXTRA snowflake — Snowpark / Core API repos ───────────────────────────
|
|
179
222
|
# Snowpark / Core API conventions (no session.sql(), canonical method names)
|
|
180
223
|
- repo: local
|
|
@@ -182,6 +225,6 @@ repos:
|
|
|
182
225
|
- id: check-snowpark-api
|
|
183
226
|
name: "[SNOWPARK-API] No session.sql(), canonical method names"
|
|
184
227
|
entry: python scripts/hooks/check_snowpark_api.py
|
|
185
|
-
language:
|
|
228
|
+
language: python
|
|
186
229
|
types: [python]
|
|
187
230
|
files: ^src/
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
3.12
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
{
|
|
2
|
+
// uv keeps this repo's environment in its own cache, OUT of the synced iCloud drive (ADR-0023),
|
|
3
|
+
// and the `.venv` here is a SYMLINK to it. VSCode's environment auto-discovery does NOT follow that
|
|
4
|
+
// symlink, so the interpreter is declared explicitly rather than picked by hand each time.
|
|
5
|
+
// ${workspaceFolder} is THIS repo when the multi-root `pinky.code-workspace` is open.
|
|
6
|
+
// Env missing? `uv sync --all-extras` recreates it.
|
|
7
|
+
"python.defaultInterpreterPath": "${workspaceFolder}/.venv/bin/python"
|
|
8
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
# CONTEXT.md — pinky-core
|
|
2
|
+
*Update date : 2026-07-12 13:12*
|
|
3
|
+
|
|
4
|
+
Single source of truth for naming in this repo. Variables, functions, modules, and prose follow it.
|
|
5
|
+
**Agent rule: read this before naming anything.**
|
|
6
|
+
|
|
7
|
+
| Term (preferred) | Avoid | Definition |
|
|
8
|
+
|---|---|---|
|
|
9
|
+
| `validate_*` (`validate_siret`, `validate_identifier`, …) | check, verify, sanitize | Reject-only — returns the input unchanged if valid, raises/returns invalid otherwise. Never transforms. |
|
|
10
|
+
| `standardize_*` (`standardize_email`, `standardize_phone`) | normalize, format | Validates **and** normalizes to a canonical form (e.g. phone → E.164). |
|
|
11
|
+
| `safe_*` (`safe_filepath`) | sanitize, escape | Same contract as `validate_*` — reject-only. Named `safe_` (not `sanitize_`) because the caller's safety comes from the *rejection*, not from any escaping/transform; a docstring once claimed it escaped quotes, it never did (coco review, 2026-07-12) — don't reintroduce that confusion. |
|
|
12
|
+
| `ref` / `refs` (`extract_table_refs`, `resolve_query`'s `refs` param) | reference(s) | A table reference string — see `FQN`. |
|
|
13
|
+
| `FQN` | fully-qualified name, qualified identifier | `DATABASE.SCHEMA.TABLE` (or `SCHEMA.TABLE`) — pinky-provider's identifier convention. A bare, unqualified name is never a legitimate FQN in this codebase (see `extract_table_refs`). |
|
|
14
|
+
| `predicate` (schedule.py's `is_business_day`, `is_eve_of_holiday`, …) | check, rule, condition | A pure `bool`-returning function taking a `date`, callable as a DAG root-task SKIP gate (ADR-0013 in pinky-provider). |
|
|
15
|
+
| `*Result` (`EmailResult`, `FecParseResult`, …) | `*Response`, `*Output` | A model wrapping the outcome of one validate/parse call — success or failure both representable, never raised. |
|
|
16
|
+
| `*Error` (`FecRowError`, `FecBalanceError`) | `*Failure`, `*Issue` | A record of one specific failure collected *during* processing (e.g. one bad row) — distinct from `*Result`, which wraps the whole call's outcome. |
|
|
17
|
+
| `lazy import` (`from unidecode import unidecode` inside a function body) | optional import, deferred import | The pattern every optional-dependency function uses so importing the *module* never requires the extra — only calling that specific function does. |
|
|
18
|
+
|
|
19
|
+
## Flagged ambiguities
|
|
20
|
+
|
|
21
|
+
- **`slugify` breaks the `to_*` prefix convention** (`to_upper_snake_case`, `to_camel_case`) — kept
|
|
22
|
+
bare because "slugify" is an established cross-language term (same name in Django, JS `slugify`
|
|
23
|
+
packages, etc.); renaming it `to_slug` would be more consistent internally but less recognizable.
|
|
24
|
+
Not revisited unless a second bare-verb name is added and the inconsistency compounds.
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: pinky-core
|
|
3
|
-
Version: 0.1.0.
|
|
3
|
+
Version: 0.1.0.dev6
|
|
4
4
|
Summary: Shared Python utilities for Snowflake development
|
|
5
5
|
Project-URL: Homepage, https://github.com/pinky-and-co/pinky-core
|
|
6
6
|
Project-URL: Repository, https://github.com/pinky-and-co/pinky-core
|
|
@@ -40,19 +40,6 @@ Requires-Dist: sqlglot
|
|
|
40
40
|
Requires-Dist: unidecode
|
|
41
41
|
Provides-Extra: address
|
|
42
42
|
Requires-Dist: usaddress; extra == 'address'
|
|
43
|
-
Provides-Extra: dev
|
|
44
|
-
Requires-Dist: bandit; extra == 'dev'
|
|
45
|
-
Requires-Dist: darglint; extra == 'dev'
|
|
46
|
-
Requires-Dist: genbadge[coverage]; extra == 'dev'
|
|
47
|
-
Requires-Dist: interrogate; extra == 'dev'
|
|
48
|
-
Requires-Dist: mkdocs-awesome-pages-plugin>=2.0; extra == 'dev'
|
|
49
|
-
Requires-Dist: mkdocs-material>=9.0; extra == 'dev'
|
|
50
|
-
Requires-Dist: mkdocstrings[python]>=0.25; extra == 'dev'
|
|
51
|
-
Requires-Dist: mypy; extra == 'dev'
|
|
52
|
-
Requires-Dist: pre-commit; extra == 'dev'
|
|
53
|
-
Requires-Dist: pytest; extra == 'dev'
|
|
54
|
-
Requires-Dist: pytest-cov; extra == 'dev'
|
|
55
|
-
Requires-Dist: ruff; extra == 'dev'
|
|
56
43
|
Provides-Extra: fec
|
|
57
44
|
Requires-Dist: unidecode; extra == 'fec'
|
|
58
45
|
Provides-Extra: lint
|
|
@@ -158,18 +145,22 @@ pinky-core/
|
|
|
158
145
|
│ ├── ci.yml
|
|
159
146
|
│ ├── publish.yml
|
|
160
147
|
│ └── release.yml
|
|
148
|
+
├── .vscode/
|
|
149
|
+
│ └── settings.json
|
|
161
150
|
├── scripts/
|
|
162
151
|
│ ├── hooks/
|
|
163
152
|
│ │ ├── badges.toml
|
|
164
153
|
│ │ ├── check_forbidden_keywords.py
|
|
165
154
|
│ │ ├── check_snowpark_api.py
|
|
166
155
|
│ │ ├── check_typed_api.py
|
|
156
|
+
│ │ ├── check_weak_assertions.py
|
|
167
157
|
│ │ ├── check_zws.py
|
|
168
158
|
│ │ ├── commit-msg
|
|
169
159
|
│ │ ├── docs_identity.json
|
|
170
160
|
│ │ ├── generate-docs
|
|
171
161
|
│ │ ├── generate_ci_badges.py
|
|
172
|
-
│ │
|
|
162
|
+
│ │ ├── render_docs.py
|
|
163
|
+
│ │ └── run-tests
|
|
173
164
|
│ └── deploy_validate_udfs.py
|
|
174
165
|
├── sql/
|
|
175
166
|
│ └── deploy_udf_example.sql
|
|
@@ -191,6 +182,13 @@ pinky-core/
|
|
|
191
182
|
│ └── xml.py
|
|
192
183
|
├── tests/
|
|
193
184
|
│ ├── fixtures/
|
|
185
|
+
│ │ ├── golden/
|
|
186
|
+
│ │ │ ├── dl_card_basic.html
|
|
187
|
+
│ │ │ ├── dl_card_expiry.html
|
|
188
|
+
│ │ │ ├── generate_html_basic.html
|
|
189
|
+
│ │ │ ├── task_table_error.html
|
|
190
|
+
│ │ │ ├── task_table_no_snowsight.html
|
|
191
|
+
│ │ │ └── task_table_snowsight.html
|
|
194
192
|
│ │ └── cats_domination.xml
|
|
195
193
|
│ ├── __init__.py
|
|
196
194
|
│ ├── test_fec.py
|
|
@@ -208,10 +206,13 @@ pinky-core/
|
|
|
208
206
|
├── .gitignore
|
|
209
207
|
├── .gitleaks.toml
|
|
210
208
|
├── .pre-commit-config.yaml
|
|
209
|
+
├── .python-version
|
|
211
210
|
├── .secrets.baseline
|
|
211
|
+
├── CONTEXT.md
|
|
212
212
|
├── LICENSE
|
|
213
213
|
├── pyproject.toml
|
|
214
|
-
|
|
214
|
+
├── README.md
|
|
215
|
+
└── uv.lock
|
|
215
216
|
```
|
|
216
217
|
|
|
217
218
|
</details>
|
|
@@ -85,18 +85,22 @@ pinky-core/
|
|
|
85
85
|
│ ├── ci.yml
|
|
86
86
|
│ ├── publish.yml
|
|
87
87
|
│ └── release.yml
|
|
88
|
+
├── .vscode/
|
|
89
|
+
│ └── settings.json
|
|
88
90
|
├── scripts/
|
|
89
91
|
│ ├── hooks/
|
|
90
92
|
│ │ ├── badges.toml
|
|
91
93
|
│ │ ├── check_forbidden_keywords.py
|
|
92
94
|
│ │ ├── check_snowpark_api.py
|
|
93
95
|
│ │ ├── check_typed_api.py
|
|
96
|
+
│ │ ├── check_weak_assertions.py
|
|
94
97
|
│ │ ├── check_zws.py
|
|
95
98
|
│ │ ├── commit-msg
|
|
96
99
|
│ │ ├── docs_identity.json
|
|
97
100
|
│ │ ├── generate-docs
|
|
98
101
|
│ │ ├── generate_ci_badges.py
|
|
99
|
-
│ │
|
|
102
|
+
│ │ ├── render_docs.py
|
|
103
|
+
│ │ └── run-tests
|
|
100
104
|
│ └── deploy_validate_udfs.py
|
|
101
105
|
├── sql/
|
|
102
106
|
│ └── deploy_udf_example.sql
|
|
@@ -118,6 +122,13 @@ pinky-core/
|
|
|
118
122
|
│ └── xml.py
|
|
119
123
|
├── tests/
|
|
120
124
|
│ ├── fixtures/
|
|
125
|
+
│ │ ├── golden/
|
|
126
|
+
│ │ │ ├── dl_card_basic.html
|
|
127
|
+
│ │ │ ├── dl_card_expiry.html
|
|
128
|
+
│ │ │ ├── generate_html_basic.html
|
|
129
|
+
│ │ │ ├── task_table_error.html
|
|
130
|
+
│ │ │ ├── task_table_no_snowsight.html
|
|
131
|
+
│ │ │ └── task_table_snowsight.html
|
|
121
132
|
│ │ └── cats_domination.xml
|
|
122
133
|
│ ├── __init__.py
|
|
123
134
|
│ ├── test_fec.py
|
|
@@ -135,10 +146,13 @@ pinky-core/
|
|
|
135
146
|
├── .gitignore
|
|
136
147
|
├── .gitleaks.toml
|
|
137
148
|
├── .pre-commit-config.yaml
|
|
149
|
+
├── .python-version
|
|
138
150
|
├── .secrets.baseline
|
|
151
|
+
├── CONTEXT.md
|
|
139
152
|
├── LICENSE
|
|
140
153
|
├── pyproject.toml
|
|
141
|
-
|
|
154
|
+
├── README.md
|
|
155
|
+
└── uv.lock
|
|
142
156
|
```
|
|
143
157
|
|
|
144
158
|
</details>
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: "0001"
|
|
3
|
+
---
|
|
4
|
+
# ADR-0001 — Zero Snowflake dependency at import time
|
|
5
|
+
Status: accepted
|
|
6
|
+
Date: 2026-05-31
|
|
7
|
+
|
|
8
|
+
## Contexte
|
|
9
|
+
|
|
10
|
+
`pinky-core` doit être importable dans tous les contextes où Python s'exécute : pipelines CI,
|
|
11
|
+
scripts de développement local, gestionnaires UDF à l'intérieur de Snowflake, et Streamlit dans les applications Snowflake.
|
|
12
|
+
L'importation inconditionnelle de Snowpark lève une erreur ou ajoute un coût de démarrage dans les contextes
|
|
13
|
+
qui n'ont pas de session active.
|
|
14
|
+
|
|
15
|
+
## Décision
|
|
16
|
+
|
|
17
|
+
Aucun module dans `pinky-core` n'importe Snowflake ou Snowpark au niveau du module.
|
|
18
|
+
Les dépendances optionnelles (`email-validator`, `phonenumbers`, `python-stdnum`, `croniter`) sont
|
|
19
|
+
importées paresseusement dans les fonctions. Les importations réservées au type vérification utilisent la garde `TYPE_CHECKING`.
|
|
20
|
+
|
|
21
|
+
## Conséquences
|
|
22
|
+
|
|
23
|
+
- `pinky-core` est installable dans n'importe quel environnement Python 3.11+ sans client
|
|
24
|
+
Snowflake — `pip install pinky-core` fonctionne en CI pur.
|
|
25
|
+
- Tout module qui a besoin d'une session active appartient à `pinky-snowpark`, pas ici.
|
|
26
|
+
- Les erreurs d'exécution dues aux dépendances optionnelles manquantes apparaissent au moment de l'appel, non à l'importation.
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
title: Features
|
|
3
3
|
---
|
|
4
4
|
# Features — pinky-core
|
|
5
|
-
*Update date : 2026-07-
|
|
5
|
+
*Update date : 2026-07-25 09:25*
|
|
6
6
|
|
|
7
7
|
Feature list ordered by priority. `stable` = in production. `planned` = not yet built, order = priority.
|
|
8
8
|
|
|
@@ -18,7 +18,7 @@ Feature list ordered by priority. `stable` = in production. `planned` = not yet
|
|
|
18
18
|
| `sql.py` | SQL parsing helpers powered by sqlglot: statement kind, table-ref extraction, query resolution, statement splitting | stable |
|
|
19
19
|
| `html.py` | HTML templating engine — emails, reports, mini-sites, Outlook-safe | stable |
|
|
20
20
|
| `xml.py` | XPath helpers, namespace-aware parsing, safe attribute extraction | stable |
|
|
21
|
-
| `files.py` | S3/MFT filename pattern normalisation — `detect_file_pattern`, `FileDetection
|
|
21
|
+
| `files.py` | S3/MFT filename pattern normalisation — `detect_file_pattern`, `FileDetection`; replaces dates, timestamps, UUIDs, ISO country codes, structured business identifiers (SIRET/SIREN/IBAN/VAT_FR), and numeric IDs with placeholders | stable |
|
|
22
22
|
| `schedule.py` | French public holidays, business-day helpers, cron human-readable formatting, Airflow-timetable predicates (month/week/fiscal-quarter, eve-of-holiday) | stable |
|
|
23
23
|
| `lint.py` | Optional lint/format delegation (sqlfluff/ruff/yamllint/detect-secrets) — consumed by pinky-provider for the secret-scan deploy gate and SQL pre-flight | stable |
|
|
24
24
|
| `warnings.py` | `PinkyDeprecationWarning` — deprecated pinky APIs and UDFs. No consumer wired yet (destined for pinky-provider's registry `status: deprecated`) | stable |
|
|
@@ -1 +1 @@
|
|
|
1
|
-
<svg xmlns="http://www.w3.org/2000/svg" width="114" height="20" role="img" aria-label="coverage: 84.
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" width="114" height="20" role="img" aria-label="coverage: 84.32%"><title>coverage: 84.32%</title><filter id="blur"><feGaussianBlur stdDeviation="16"/></filter><linearGradient id="s" x2="0" y2="100%"><stop offset="0" stop-color="#bbb" stop-opacity=".1"/><stop offset="1" stop-opacity=".1"/></linearGradient><clipPath id="r"><rect width="114" height="20" rx="3"/></clipPath><g clip-path="url(#r)"><rect width="61" height="20" fill="#555"/><rect x="61" width="53" height="20" fill="#67ac09"/><rect width="114" height="20" fill="url(#s)"/></g><g fill="#fff" text-anchor="middle" font-family="Verdana,Geneva,DejaVu Sans,sans-serif" text-rendering="geometricPrecision" font-size="110"><g transform="scale(.1)"><g aria-hidden="true" fill="#010101"><text x="315" y="150" fill-opacity=".8" filter="url(#blur)" textLength="510">coverage</text><text x="315" y="150" fill-opacity=".3" textLength="510">coverage</text></g><text x="315" y="140" textLength="510">coverage</text></g><g transform="scale(.1)"><g aria-hidden="true" fill="#010101"><text x="865" y="150" fill-opacity=".8" filter="url(#blur)" textLength="430">84.32%</text><text x="865" y="150" fill-opacity=".3" textLength="430">84.32%</text></g><text x="865" y="140" textLength="430">84.32%</text></g></g></svg>
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
<svg xmlns="http://www.w3.org/2000/svg" width="81" height="20">
|
|
2
|
-
<title>generate-docs · mdformat · check-zws · check-forbidden-keywords · commit-msg-format · mypy · interrogate · darglint · check-typed-api · check-snowpark-api</title>
|
|
2
|
+
<title>generate-docs · run-tests · mdformat · check-zws · check-forbidden-keywords · commit-msg-format · mypy · interrogate · darglint · check-typed-api · check-weak-assertions · check-snowpark-api</title>
|
|
3
3
|
<rect width="81" height="20" rx="3" fill="#555"/>
|
|
4
4
|
<rect x="50" width="31" height="20" rx="3" fill="#007ec6"/>
|
|
5
5
|
<rect x="50" width="4" height="20" fill="#007ec6"/>
|
|
6
6
|
<g fill="#fff" text-anchor="middle" font-family="DejaVu Sans,Verdana,Geneva,sans-serif" font-size="11">
|
|
7
7
|
<text x="25" y="15" fill="#010101" fill-opacity=".3">hooks</text>
|
|
8
8
|
<text x="25" y="14">hooks</text>
|
|
9
|
-
<text x="65" y="15" fill="#010101" fill-opacity=".3">
|
|
10
|
-
<text x="65" y="14">
|
|
9
|
+
<text x="65" y="15" fill="#010101" fill-opacity=".3">12</text>
|
|
10
|
+
<text x="65" y="14">12</text>
|
|
11
11
|
</g>
|
|
12
12
|
</svg>
|
|
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "pinky-core"
|
|
7
|
-
version = "0.1.0.
|
|
7
|
+
version = "0.1.0.dev6"
|
|
8
8
|
description = "Shared Python utilities for Snowflake development"
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
license = { file = "LICENSE" }
|
|
@@ -32,7 +32,18 @@ xml = ["lxml", "lxml-stubs"]
|
|
|
32
32
|
fec = ["unidecode"]
|
|
33
33
|
# Delegated lint/format/scan backends for pinky_core.lint (SQL / YAML / Python / secrets).
|
|
34
34
|
lint = ["sqlfluff>=3.0", "yamllint>=1.35", "ruff", "detect-secrets>=1.5"]
|
|
35
|
-
|
|
35
|
+
|
|
36
|
+
# Deps de dev en dependency-group (PEP 735), PAS en extra (2026-07-22, repo pilote).
|
|
37
|
+
# `uv run` resynchronise l'environnement vers l'état PAR DÉFAUT : les extras n'en font pas partie, il
|
|
38
|
+
# les DÉSINSTALLE donc silencieusement, puis retombe sur le premier pytest trouvé sur le PATH — c'est
|
|
39
|
+
# ce qui avait produit un « 1180 tests au vert » qui validait en réalité les envs conda. Le groupe
|
|
40
|
+
# `dev` de PEP 735, lui, est synchronisé par défaut : `uv run` redevient correct sans aucun flag à
|
|
41
|
+
# retenir. (`[tool.uv] default-extras` est accepté sans erreur mais reste sans effet — testé.)
|
|
42
|
+
# Côté CI, l'équivalent de `-e ".[dev]"` est `-e . --group dev` (pip >= 25.1 ; ensurepip livre 26.1.2
|
|
43
|
+
# avec Python 3.12, donc aucune contrainte à gérer). Un extra reste le bon outil pour une option
|
|
44
|
+
# destinée aux CONSOMMATEURS de la lib — validate/address/schedule/xml/fec/lint restent des extras.
|
|
45
|
+
[dependency-groups]
|
|
46
|
+
dev = [
|
|
36
47
|
"mkdocs-material>=9.0",
|
|
37
48
|
"mkdocs-awesome-pages-plugin>=2.0",
|
|
38
49
|
"mkdocstrings[python]>=0.25",
|
|
@@ -52,6 +63,14 @@ Homepage = "https://github.com/pinky-and-co/pinky-core"
|
|
|
52
63
|
Repository = "https://github.com/pinky-and-co/pinky-core"
|
|
53
64
|
Issues = "https://github.com/pinky-and-co/pinky-core/issues"
|
|
54
65
|
|
|
66
|
+
# ── uv ───────────────────────────────────────────────────────────────────────
|
|
67
|
+
# The project environment lives in uv's cache, NOT in a `.venv` at the repo root: this workspace sits
|
|
68
|
+
# on a synced iCloud drive, and ADR-0023 recorded confirmed corruption when a tool environment lives
|
|
69
|
+
# inside one. Preview feature, accepted knowingly — `uv sync` prints the resolved path, so re-check it
|
|
70
|
+
# still lands under ~/.cache/uv after a uv upgrade.
|
|
71
|
+
[tool.uv]
|
|
72
|
+
preview-features = ["centralized-project-envs"]
|
|
73
|
+
|
|
55
74
|
[tool.hatch.build.targets.wheel]
|
|
56
75
|
packages = ["src/pinky_core"]
|
|
57
76
|
|