pinky-core 0.1.0.dev1__tar.gz → 0.1.0.dev3__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.
Files changed (92) hide show
  1. pinky_core-0.1.0.dev3/.github/workflows/ci.yml +62 -0
  2. {pinky_core-0.1.0.dev1 → pinky_core-0.1.0.dev3}/.github/workflows/publish.yml +2 -2
  3. {pinky_core-0.1.0.dev1 → pinky_core-0.1.0.dev3}/.github/workflows/release.yml +2 -2
  4. {pinky_core-0.1.0.dev1 → pinky_core-0.1.0.dev3}/.gitignore +4 -12
  5. pinky_core-0.1.0.dev3/.gitleaks.toml +14 -0
  6. pinky_core-0.1.0.dev3/.pre-commit-config.yaml +187 -0
  7. pinky_core-0.1.0.dev3/PKG-INFO +228 -0
  8. pinky_core-0.1.0.dev3/README.md +155 -0
  9. pinky_core-0.1.0.dev3/docs/.pages +7 -0
  10. pinky_core-0.1.0.dev3/docs/adr/.pages +2 -0
  11. {pinky_core-0.1.0.dev1 → pinky_core-0.1.0.dev3}/docs/adr/0001-zero-dependency-static-html.md +3 -0
  12. pinky_core-0.1.0.dev3/docs/explanation/.pages +2 -0
  13. pinky_core-0.1.0.dev1/docs/explanation/design.md → pinky_core-0.1.0.dev3/docs/explanation/1-design.md +11 -4
  14. pinky_core-0.1.0.dev1/docs/explanation/features.md → pinky_core-0.1.0.dev3/docs/explanation/2-features.md +7 -18
  15. pinky_core-0.1.0.dev3/docs/how-to/.pages +2 -0
  16. pinky_core-0.1.0.dev3/docs/how-to/index.md +1 -0
  17. pinky_core-0.1.0.dev3/docs/index.md +23 -0
  18. pinky_core-0.1.0.dev3/docs/mkdocs.yml +24 -0
  19. pinky_core-0.1.0.dev3/docs/reference/.pages +1 -0
  20. pinky_core-0.1.0.dev3/docs/reference/api/.pages +2 -0
  21. pinky_core-0.1.0.dev3/docs/reference/api/fec.md +5 -0
  22. pinky_core-0.1.0.dev3/docs/reference/api/html.md +5 -0
  23. pinky_core-0.1.0.dev3/docs/reference/api/lint.md +5 -0
  24. pinky_core-0.1.0.dev3/docs/reference/api/warnings.md +5 -0
  25. pinky_core-0.1.0.dev3/docs/reference/generated/coverage_badge.svg +1 -0
  26. pinky_core-0.1.0.dev3/docs/reference/generated/hooks_badge.svg +12 -0
  27. pinky_core-0.1.0.dev3/docs/reference/generated/interrogate_badge.svg +58 -0
  28. pinky_core-0.1.0.dev3/docs/reference/generated/noqa_badge.svg +12 -0
  29. pinky_core-0.1.0.dev3/docs/tutorials/.pages +2 -0
  30. pinky_core-0.1.0.dev3/docs/tutorials/index.md +4 -0
  31. {pinky_core-0.1.0.dev1 → pinky_core-0.1.0.dev3}/pyproject.toml +44 -7
  32. {pinky_core-0.1.0.dev1 → pinky_core-0.1.0.dev3}/scripts/deploy_validate_udfs.py +4 -4
  33. pinky_core-0.1.0.dev3/scripts/hooks/badges.toml +84 -0
  34. pinky_core-0.1.0.dev3/scripts/hooks/check_forbidden_keywords.py +199 -0
  35. pinky_core-0.1.0.dev3/scripts/hooks/check_snowpark_api.py +278 -0
  36. {pinky_core-0.1.0.dev1 → pinky_core-0.1.0.dev3}/scripts/hooks/check_typed_api.py +11 -1
  37. {pinky_core-0.1.0.dev1 → pinky_core-0.1.0.dev3}/scripts/hooks/check_zws.py +25 -1
  38. {pinky_core-0.1.0.dev1 → pinky_core-0.1.0.dev3}/scripts/hooks/commit-msg +42 -15
  39. pinky_core-0.1.0.dev3/scripts/hooks/docs_identity.json +18 -0
  40. pinky_core-0.1.0.dev3/scripts/hooks/generate-docs +48 -0
  41. pinky_core-0.1.0.dev3/scripts/hooks/generate_ci_badges.py +152 -0
  42. pinky_core-0.1.0.dev3/scripts/hooks/render_docs.py +605 -0
  43. {pinky_core-0.1.0.dev1 → pinky_core-0.1.0.dev3}/src/pinky_core/__init__.py +51 -3
  44. pinky_core-0.1.0.dev3/src/pinky_core/finance/__init__.py +33 -0
  45. pinky_core-0.1.0.dev3/src/pinky_core/finance/fec.py +551 -0
  46. {pinky_core-0.1.0.dev1 → pinky_core-0.1.0.dev3}/src/pinky_core/fmt.py +8 -1
  47. pinky_core-0.1.0.dev3/src/pinky_core/html.py +350 -0
  48. pinky_core-0.1.0.dev3/src/pinky_core/lint.py +287 -0
  49. {pinky_core-0.1.0.dev1 → pinky_core-0.1.0.dev3}/src/pinky_core/schedule.py +220 -8
  50. pinky_core-0.1.0.dev3/src/pinky_core/sql.py +127 -0
  51. {pinky_core-0.1.0.dev1 → pinky_core-0.1.0.dev3}/src/pinky_core/validate.py +19 -10
  52. pinky_core-0.1.0.dev3/src/pinky_core/warnings.py +23 -0
  53. {pinky_core-0.1.0.dev1 → pinky_core-0.1.0.dev3}/src/pinky_core/xml.py +5 -2
  54. pinky_core-0.1.0.dev3/tests/test_fec.py +310 -0
  55. {pinky_core-0.1.0.dev1 → pinky_core-0.1.0.dev3}/tests/test_files.py +5 -5
  56. pinky_core-0.1.0.dev3/tests/test_html.py +63 -0
  57. pinky_core-0.1.0.dev3/tests/test_lint.py +104 -0
  58. {pinky_core-0.1.0.dev1 → pinky_core-0.1.0.dev3}/tests/test_schedule.py +121 -1
  59. {pinky_core-0.1.0.dev1 → pinky_core-0.1.0.dev3}/tests/test_sql.py +27 -29
  60. pinky_core-0.1.0.dev3/tests/test_warnings.py +34 -0
  61. pinky_core-0.1.0.dev1/.github/workflows/ci.yml +0 -39
  62. pinky_core-0.1.0.dev1/.pre-commit-config.yaml +0 -125
  63. pinky_core-0.1.0.dev1/.vscode/extensions.json +0 -35
  64. pinky_core-0.1.0.dev1/.vscode/settings.json +0 -46
  65. pinky_core-0.1.0.dev1/AGENT.md +0 -209
  66. pinky_core-0.1.0.dev1/PKG-INFO +0 -87
  67. pinky_core-0.1.0.dev1/README.md +0 -30
  68. pinky_core-0.1.0.dev1/docs/index.md +0 -15
  69. pinky_core-0.1.0.dev1/mkdocs.dev.yml +0 -3
  70. pinky_core-0.1.0.dev1/mkdocs.yml +0 -35
  71. pinky_core-0.1.0.dev1/scripts/hooks/check-docs-up-to-date +0 -36
  72. pinky_core-0.1.0.dev1/scripts/hooks/check_forbidden_keywords.py +0 -76
  73. pinky_core-0.1.0.dev1/src/pinky_core/sql.py +0 -83
  74. pinky_core-0.1.0.dev1/src/pinky_core/sql_custom.py +0 -78
  75. {pinky_core-0.1.0.dev1 → pinky_core-0.1.0.dev3}/.secrets.baseline +0 -0
  76. {pinky_core-0.1.0.dev1 → pinky_core-0.1.0.dev3}/LICENSE +0 -0
  77. {pinky_core-0.1.0.dev1/docs/reference/generated → pinky_core-0.1.0.dev3/docs/reference}/api/files.md +0 -0
  78. {pinky_core-0.1.0.dev1/docs/reference/generated → pinky_core-0.1.0.dev3/docs/reference}/api/fmt.md +0 -0
  79. {pinky_core-0.1.0.dev1/docs/reference/generated → pinky_core-0.1.0.dev3/docs/reference}/api/schedule.md +0 -0
  80. {pinky_core-0.1.0.dev1/docs/reference/generated → pinky_core-0.1.0.dev3/docs/reference}/api/security.md +0 -0
  81. {pinky_core-0.1.0.dev1/docs/reference/generated → pinky_core-0.1.0.dev3/docs/reference}/api/sql.md +0 -0
  82. {pinky_core-0.1.0.dev1/docs/reference/generated → pinky_core-0.1.0.dev3/docs/reference}/api/validate.md +0 -0
  83. {pinky_core-0.1.0.dev1/docs/reference/generated → pinky_core-0.1.0.dev3/docs/reference}/api/xml.md +0 -0
  84. {pinky_core-0.1.0.dev1 → pinky_core-0.1.0.dev3}/sql/deploy_udf_example.sql +0 -0
  85. {pinky_core-0.1.0.dev1 → pinky_core-0.1.0.dev3}/src/pinky_core/files.py +0 -0
  86. {pinky_core-0.1.0.dev1 → pinky_core-0.1.0.dev3}/src/pinky_core/security.py +0 -0
  87. {pinky_core-0.1.0.dev1 → pinky_core-0.1.0.dev3}/tests/__init__.py +0 -0
  88. {pinky_core-0.1.0.dev1 → pinky_core-0.1.0.dev3}/tests/fixtures/cats_domination.xml +0 -0
  89. {pinky_core-0.1.0.dev1 → pinky_core-0.1.0.dev3}/tests/test_fmt.py +0 -0
  90. {pinky_core-0.1.0.dev1 → pinky_core-0.1.0.dev3}/tests/test_security.py +0 -0
  91. {pinky_core-0.1.0.dev1 → pinky_core-0.1.0.dev3}/tests/test_validate.py +0 -0
  92. {pinky_core-0.1.0.dev1 → pinky_core-0.1.0.dev3}/tests/test_xml_xpath.py +0 -0
@@ -0,0 +1,62 @@
1
+ name: CI
2
+
3
+ on:
4
+ push:
5
+ branches: [main, develop]
6
+ paths: ['src/**', 'tests/**', 'pyproject.toml', 'scripts/**', '.pre-commit-config.yaml', '.github/workflows/ci.yml']
7
+ pull_request:
8
+ branches: [main, develop]
9
+ paths: ['src/**', 'tests/**', 'pyproject.toml', 'scripts/**', '.pre-commit-config.yaml', '.github/workflows/ci.yml']
10
+
11
+ jobs:
12
+ ci:
13
+ runs-on: ubuntu-latest
14
+ env:
15
+ FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: true
16
+
17
+ steps:
18
+ - uses: actions/checkout@v4
19
+
20
+ - name: Set up Python 3.12
21
+ uses: actions/setup-python@v5
22
+ with:
23
+ python-version: "3.12"
24
+
25
+ - name: Run pre-commit hooks
26
+ # Single source of truth: .pre-commit-config.yaml (ruff, mypy, bandit, gitleaks,
27
+ # detect-secrets, check-zws, check-forbidden-keywords, interrogate, darglint,
28
+ # check-typed-api, mdformat) — replaces the hand-duplicated Lint/Format/Type/Security
29
+ # steps, which only re-implemented 3 of the ~11 hooks and could silently drift from
30
+ # .pre-commit-config.yaml's actual args/excludes. j178/prek-action (not `pip install
31
+ # prek`): official action, caches the prek env between runs, defaults to
32
+ # `prek run --show-diff-on-failure --color=always --all-files`.
33
+ uses: j178/prek-action@v2
34
+
35
+ - name: Install dependencies
36
+ run: pip install -e ".[validate,address,dev]"
37
+
38
+ - name: Tests
39
+ run: pytest
40
+
41
+ precommit-windows:
42
+ # ADR-0023 pilot gate: hermetic pre-commit hooks must run on the brother's
43
+ # machine (Windows, prek only, no toolchain) — this is the real gate, not the Mac.
44
+ runs-on: windows-latest
45
+ strategy:
46
+ matrix:
47
+ shell: [cmd, pwsh]
48
+ defaults:
49
+ run:
50
+ shell: ${{ matrix.shell }}
51
+ steps:
52
+ - uses: actions/checkout@v4
53
+
54
+ - name: Set up Python 3.12
55
+ uses: actions/setup-python@v5
56
+ with:
57
+ python-version: "3.12"
58
+
59
+ - name: Run mdformat hook
60
+ uses: j178/prek-action@v2
61
+ with:
62
+ extra-args: 'mdformat --all-files'
@@ -17,10 +17,10 @@ jobs:
17
17
  steps:
18
18
  - uses: actions/checkout@v4
19
19
 
20
- - name: Set up Python 3.11
20
+ - name: Set up Python 3.12
21
21
  uses: actions/setup-python@v5
22
22
  with:
23
- python-version: "3.11"
23
+ python-version: "3.12"
24
24
 
25
25
  - name: Install build
26
26
  run: pip install build
@@ -32,10 +32,10 @@ jobs:
32
32
  git tag "v${{ steps.version.outputs.version }}"
33
33
  git push origin "v${{ steps.version.outputs.version }}"
34
34
 
35
- - name: Set up Python 3.11
35
+ - name: Set up Python 3.12
36
36
  uses: actions/setup-python@v5
37
37
  with:
38
- python-version: "3.11"
38
+ python-version: "3.12"
39
39
 
40
40
  - name: Install build
41
41
  run: pip install build
@@ -17,26 +17,17 @@ venv/
17
17
  .conda/
18
18
 
19
19
  # ── Tools cache ──────────────────────────────────────────────────────────────
20
- .ruff_cache/
21
- .mypy_cache/
22
- .pytest_cache/
20
+ .cache/*
23
21
  .pre-commit-cache/
24
- htmlcov/
25
- .coverage
26
- .coverage.*
27
- coverage.xml
28
22
 
29
23
  # ── Generated docs ───────────────────────────────────────────────────────────
30
- # docs/api/ is versioned — regenerate with: pdoc snowflake_kit -o docs/api/ or wget
24
+ # docs/api/ is versioned — regenerate with: pdoc pinky_core -o docs/api/ or wget
31
25
  # Heavy wget mirrors are excluded (1 GB+)
32
26
  docs/api/*/*.com/
33
27
  # Local MkDocs assets — symlinked from pinky-studio for local dev, not committed
34
28
  docs/assets/
35
29
  site/
36
-
37
- # ── Provider cache ───────────────────────────────────────────────────────────
38
- .snowflake-provider/
39
-
30
+ site.nosync/
40
31
  # ── Local dev ────────────────────────────────────────────────────────────────
41
32
  .local/
42
33
  *.local.json
@@ -51,6 +42,7 @@ connections.toml
51
42
  .DS_Store
52
43
  .AppleDouble
53
44
  .LSOverride
45
+ *.icloud
54
46
 
55
47
  # ── VSCode ───────────────────────────────────────────────────────────────────
56
48
  # (keep .vscode/extensions.json and .vscode/settings.json — versioned)
@@ -0,0 +1,14 @@
1
+ # Distributed as-is to every onboarded repo's root (see setup/precommit.py distribute()).
2
+ # gitleaks auto-discovers .gitleaks.toml in the scan root — no pre-commit-side wiring needed.
3
+ [extend]
4
+ useDefault = true
5
+
6
+ [allowlist]
7
+ # .secrets.baseline stores detect-secrets' accepted findings as SHA1 hashes (hashed_secret),
8
+ # never the raw secret — that's the whole point of a baseline. A high-entropy hash next to a
9
+ # "secret"-shaped key still trips gitleaks' generic-api-key heuristic the moment a repo's baseline
10
+ # records its first real finding (empty result sets don't trigger it, which is why this went
11
+ # unnoticed until pinky-studio).
12
+ paths = [
13
+ '''\.secrets\.baseline''',
14
+ ]
@@ -0,0 +1,187 @@
1
+ ---
2
+ # =============================================================================
3
+ # .pre-commit-config.yaml — GENERATED by pinky-workspace/setup.py — DO NOT EDIT
4
+ # =============================================================================
5
+ # Source: pinky-workspace/core/pre-commit/{base,extras/*}.yaml
6
+ # Third-party revs are pinned centrally in this base — bump them here, once.
7
+ # To change which hooks a repo gets, edit the workspace source and re-run setup.py.
8
+ #
9
+ # Install (done automatically by setup.py):
10
+ # pip install pre-commit
11
+ # pre-commit install
12
+ # pre-commit install --hook-type commit-msg
13
+ # =============================================================================
14
+ default_install_hook_types: [pre-commit, commit-msg]
15
+ # Pin explicite : sans ça, les hooks `language: python` (mdformat, mypy, interrogate, darglint…)
16
+ # créent leur venv avec le python3 ambiant du PATH au moment de l'install, pas une version garantie
17
+ # par le repo — non-déterministe en local (CI est protégée par actions/setup-python en amont).
18
+ default_language_version:
19
+ python: python3.12
20
+
21
+ repos:
22
+ # ── Formatting: trailing spaces + newline ─────────────────────────────────
23
+ - repo: https://github.com/pre-commit/pre-commit-hooks
24
+ rev: v6.0.0
25
+ hooks:
26
+ - id: trailing-whitespace
27
+ - id: end-of-file-fixer
28
+ exclude: ^docs/reference/generated/
29
+ - id: check-yaml
30
+ - id: check-toml
31
+ - id: check-merge-conflict
32
+ - id: debug-statements
33
+
34
+ # ── Docs generation: README + docs/index.md + quality badges ──────────────
35
+ # Universal (every repo, not just the snowflake extra — 2026-07-10, renamed/extended from
36
+ # check-docs-up-to-date). Runs before mdformat below so the regenerated README gets reformatted.
37
+ - repo: local
38
+ hooks:
39
+ - id: generate-docs
40
+ name: Generate docs (README + index.md + badges)
41
+ entry: scripts/hooks/generate-docs
42
+ language: script
43
+ stages: [pre-commit]
44
+ pass_filenames: false
45
+
46
+ # ── Formatting: Markdown ──────────────────────────────────────────────────
47
+ - repo: local
48
+ hooks:
49
+ - id: mdformat
50
+ name: mdformat
51
+ entry: mdformat
52
+ language: python
53
+ additional_dependencies: ["mdformat==1.0.0"]
54
+ files: ^README\.md$
55
+
56
+ # ── Security: secrets & forbidden files ──────────────────────────────────
57
+ # gitleaks' hook has pass_filenames: false (it scans the repo itself, not a file list pre-commit
58
+ # hands it) — a pre-commit `exclude:` here would be silently inert. The real exclusion mechanism
59
+ # is the distributed .gitleaks.toml allowlist (see distribute()/DISTRIBUTED_PATHS): .secrets.baseline
60
+ # stores detect-secrets' accepted findings as SHA1 hashes, never the raw secret, but a high-entropy
61
+ # hash next to a "secret"-shaped key still trips gitleaks' generic-api-key heuristic (found rolling
62
+ # this out to pinky-studio, the first repo whose baseline had a real finding instead of an empty
63
+ # result set).
64
+ - repo: https://github.com/gitleaks/gitleaks
65
+ rev: v8.30.0
66
+ hooks:
67
+ - id: gitleaks
68
+
69
+ # ── Security: alternative secret detection ────────────────────────────────
70
+ - repo: https://github.com/Yelp/detect-secrets
71
+ rev: v1.5.0
72
+ hooks:
73
+ - id: detect-secrets
74
+ args: ["--baseline", ".secrets.baseline"]
75
+ exclude: ^docs/
76
+
77
+ # ── Zero-width spaces (copy-paste from Outlook/Teams) ────────────────────
78
+ - repo: local
79
+ hooks:
80
+ - id: check-zws
81
+ name: "[ZWS] Invisible characters (zero-width)"
82
+ entry: python scripts/hooks/check_zws.py
83
+ language: system
84
+ pass_filenames: true
85
+
86
+ # ── Employer reference guard ──────────────────────────────────────────────
87
+ - repo: local
88
+ hooks:
89
+ - id: check-forbidden-keywords
90
+ name: "[FORBIDDEN] Employer references"
91
+ entry: python scripts/hooks/check_forbidden_keywords.py
92
+ language: system
93
+ pass_filenames: true
94
+
95
+ # ── Commit message format ─────────────────────────────────────────────────
96
+ - repo: local
97
+ hooks:
98
+ - id: commit-msg-format
99
+ name: Commit message format
100
+ entry: scripts/hooks/commit-msg
101
+ language: script
102
+ stages: [commit-msg]
103
+
104
+ # ── EXTRA python — repos with src/ ────────────────────────────────────────
105
+ # Python: ruff lint + format
106
+ - repo: https://github.com/astral-sh/ruff-pre-commit
107
+ rev: v0.15.15
108
+ hooks:
109
+ - id: ruff-check
110
+ args: [--no-fix]
111
+ - id: ruff-format
112
+ args: [--check]
113
+
114
+ # Python: type checking
115
+ - repo: local
116
+ hooks:
117
+ - id: mypy
118
+ name: mypy
119
+ entry: mypy
120
+ # Hermetic (ADR-0026, supersedes ADR-0023's system carve-out): additional_dependencies is
121
+ # auto-derived per repo by setup.py (build_precommit_config) from THAT repo's own
122
+ # pyproject.toml runtime `dependencies` — mypy pin + real third-party deps, minus internal
123
+ # pinky-* packages (pre-release, not on PyPI, excluded from the derivation).
124
+ # --ignore-missing-imports stays as the residual fallback for those excluded imports only.
125
+ language: python
126
+ additional_dependencies: ["mypy==2.0.0", "pydantic>=2.0", "sqlglot", "unidecode"]
127
+ types: [python]
128
+ args: [--ignore-missing-imports]
129
+ files: ^src/
130
+ # mypy's incremental cache (.mypy_cache/) isn't safe under concurrent access — without
131
+ # this, prek/pre-commit's default file-batch parallelism spawns multiple mypy processes
132
+ # against the same cache dir and one crashes with INTERNAL ERROR (reproduced on
133
+ # pinky-core: a single-process run over the same files passes clean).
134
+ require_serial: true
135
+
136
+ # Python: security
137
+ # B108 (hardcoded /tmp) is skipped workspace-wide: Snowflake execution only exposes
138
+ # /tmp as a writable scratch directory, so it is the sanctioned path, not a smell.
139
+ - repo: https://github.com/PyCQA/bandit
140
+ rev: "1.9.4"
141
+ hooks:
142
+ - id: bandit
143
+ pass_filenames: false
144
+ args: [-r, src/, -ll, --skip, B108]
145
+
146
+ # Docstrings: coverage
147
+ - repo: local
148
+ hooks:
149
+ - id: interrogate
150
+ name: "[DOCSTRING] Coverage"
151
+ entry: interrogate
152
+ language: python
153
+ additional_dependencies: ["interrogate==1.7.0"]
154
+ pass_filenames: false
155
+ args: [src/, --ignore-init-method, --ignore-magic, --ignore-module, --ignore-private]
156
+
157
+ # Docstrings: args/returns match signature
158
+ - repo: local
159
+ hooks:
160
+ - id: darglint
161
+ name: "[DOCSTRING] Args/Returns match"
162
+ entry: darglint
163
+ language: python
164
+ additional_dependencies: ["darglint==1.8.1"]
165
+ types: [python]
166
+ files: ^src/
167
+
168
+ # Typed API guard
169
+ - repo: local
170
+ hooks:
171
+ - id: check-typed-api
172
+ name: "[TYPED-API] No raw dict/tuple/set in public annotations"
173
+ entry: python scripts/hooks/check_typed_api.py
174
+ language: system
175
+ types: [python]
176
+ files: ^src/
177
+
178
+ # ── EXTRA snowflake — Snowpark / Core API repos ───────────────────────────
179
+ # Snowpark / Core API conventions (no session.sql(), canonical method names)
180
+ - repo: local
181
+ hooks:
182
+ - id: check-snowpark-api
183
+ name: "[SNOWPARK-API] No session.sql(), canonical method names"
184
+ entry: python scripts/hooks/check_snowpark_api.py
185
+ language: system
186
+ types: [python]
187
+ files: ^src/
@@ -0,0 +1,228 @@
1
+ Metadata-Version: 2.4
2
+ Name: pinky-core
3
+ Version: 0.1.0.dev3
4
+ Summary: Shared Python utilities for Snowflake development
5
+ Project-URL: Homepage, https://github.com/pinky-and-co/pinky-core
6
+ Project-URL: Repository, https://github.com/pinky-and-co/pinky-core
7
+ Project-URL: Issues, https://github.com/pinky-and-co/pinky-core/issues
8
+ Author: pinky-and-co
9
+ License: MIT License
10
+
11
+ Copyright (c) 2026 pinky-and-co
12
+
13
+ Permission is hereby granted, free of charge, to any person obtaining a copy
14
+ of this software and associated documentation files (the "Software"), to deal
15
+ in the Software without restriction, including without limitation the rights
16
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
17
+ copies of the Software, and to permit persons to whom the Software is
18
+ furnished to do so, subject to the following conditions:
19
+
20
+ The above copyright notice and this permission notice shall be included in all
21
+ copies or substantial portions of the Software.
22
+
23
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
24
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
25
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
26
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
27
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
28
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
29
+ SOFTWARE.
30
+ License-File: LICENSE
31
+ Keywords: data-engineering,snowflake,snowpark,utilities
32
+ Classifier: License :: OSI Approved :: MIT License
33
+ Classifier: Operating System :: OS Independent
34
+ Classifier: Programming Language :: Python :: 3.12
35
+ Classifier: Topic :: Database
36
+ Classifier: Topic :: Software Development :: Libraries :: Python Modules
37
+ Requires-Python: >=3.12
38
+ Requires-Dist: pydantic>=2.0
39
+ Requires-Dist: sqlglot
40
+ Requires-Dist: unidecode
41
+ Provides-Extra: address
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
+ Provides-Extra: fec
57
+ Requires-Dist: unidecode; extra == 'fec'
58
+ Provides-Extra: lint
59
+ Requires-Dist: detect-secrets>=1.5; extra == 'lint'
60
+ Requires-Dist: ruff; extra == 'lint'
61
+ Requires-Dist: sqlfluff>=3.0; extra == 'lint'
62
+ Requires-Dist: yamllint>=1.35; extra == 'lint'
63
+ Provides-Extra: schedule
64
+ Requires-Dist: croniter; extra == 'schedule'
65
+ Provides-Extra: validate
66
+ Requires-Dist: email-validator; extra == 'validate'
67
+ Requires-Dist: phonenumbers; extra == 'validate'
68
+ Requires-Dist: python-stdnum; extra == 'validate'
69
+ Provides-Extra: xml
70
+ Requires-Dist: lxml; extra == 'xml'
71
+ Requires-Dist: lxml-stubs; extra == 'xml'
72
+ Description-Content-Type: text/markdown
73
+
74
+ # pinky-core
75
+
76
+ <!-- pinky:readme-badges:start -->
77
+
78
+ <div align="center">
79
+
80
+ <img src="https://assets.pinky-suite.fr/icons/pinky-header.png" width="380" alt="pinky &amp; co">
81
+
82
+ ![CI](https://github.com/pinky-and-co/pinky-core/actions/workflows/ci.yml/badge.svg)
83
+ ![Coverage](docs/reference/generated/coverage_badge.svg)
84
+ ![PyPI](https://img.shields.io/pypi/v/pinky-core)
85
+ ![License](https://img.shields.io/badge/license-MIT-green)
86
+ ![Code style](https://img.shields.io/endpoint?url=https://raw.githubusercontent.com/astral-sh/ruff/main/assets/badge/v2.json)
87
+ [![prek](https://img.shields.io/endpoint?url=https://raw.githubusercontent.com/j178/prek/master/docs/assets/badge-v0.json)](https://github.com/j178/prek)
88
+
89
+ **[Install](#install) • [Documentation](#documentation) • [License](#license)**
90
+
91
+ </div>
92
+ <!-- pinky:readme-badges:end -->
93
+
94
+ <!-- pinky:readme-tagline:start -->
95
+
96
+ Shared Python utilities for Snowflake development.
97
+
98
+ Part of the [pinky-and-co](https://github.com/pinky-and-co) suite.
99
+
100
+ <!-- pinky:readme-tagline:end -->
101
+
102
+ ______________________________________________________________________
103
+
104
+ ## Why pinky?
105
+
106
+ <!-- pinky:readme-why:start -->
107
+
108
+ The modern data stack was built on a good idea — **best of breed**. One tool for ingestion, one for transformation, one for orchestration, one for observability.
109
+
110
+ The reality: more licenses, same delivery time, and most of your energy goes into *the glue*.
111
+
112
+ Snowflake collapsed the stack. **Pinky makes it operational.**
113
+
114
+ Do it right, and the credits follow.
115
+
116
+ **Rationalized costs · Pure Python · Snowflake Native First**
117
+
118
+ <!-- pinky:readme-why:end -->
119
+
120
+ ______________________________________________________________________
121
+
122
+ ## Install
123
+
124
+ ```bash
125
+ pip install pinky-core
126
+ pip install "pinky-core[validate]" # with SIRET, IBAN, email, phone validators
127
+ pip install "pinky-core[address]" # with address parsing
128
+ ```
129
+
130
+ ______________________________________________________________________
131
+
132
+ ## Documentation
133
+
134
+ <!-- pinky:readme-docs:start -->
135
+
136
+ **Explanation**
137
+
138
+ - [Design](docs/explanation/1-design.md)
139
+ - [Features](docs/explanation/2-features.md)
140
+
141
+ **API reference**
142
+
143
+ - [Generated API docs](docs/reference/api/)
144
+
145
+ **Decisions**
146
+
147
+ - [ADRs](docs/adr/)
148
+
149
+ **Structure**
150
+
151
+ <details>
152
+ <summary>Show the directory tree</summary>
153
+
154
+ ```text
155
+ pinky-core/
156
+ ├── .github/
157
+ │ └── workflows/
158
+ │ ├── ci.yml
159
+ │ ├── publish.yml
160
+ │ └── release.yml
161
+ ├── scripts/
162
+ │ ├── hooks/
163
+ │ │ ├── badges.toml
164
+ │ │ ├── check_forbidden_keywords.py
165
+ │ │ ├── check_snowpark_api.py
166
+ │ │ ├── check_typed_api.py
167
+ │ │ ├── check_zws.py
168
+ │ │ ├── commit-msg
169
+ │ │ ├── docs_identity.json
170
+ │ │ ├── generate-docs
171
+ │ │ ├── generate_ci_badges.py
172
+ │ │ └── render_docs.py
173
+ │ └── deploy_validate_udfs.py
174
+ ├── sql/
175
+ │ └── deploy_udf_example.sql
176
+ ├── src/
177
+ │ └── pinky_core/
178
+ │ ├── finance/
179
+ │ │ ├── __init__.py
180
+ │ │ └── fec.py
181
+ │ ├── __init__.py
182
+ │ ├── files.py
183
+ │ ├── fmt.py
184
+ │ ├── html.py
185
+ │ ├── lint.py
186
+ │ ├── schedule.py
187
+ │ ├── security.py
188
+ │ ├── sql.py
189
+ │ ├── validate.py
190
+ │ ├── warnings.py
191
+ │ └── xml.py
192
+ ├── tests/
193
+ │ ├── fixtures/
194
+ │ │ └── cats_domination.xml
195
+ │ ├── __init__.py
196
+ │ ├── test_fec.py
197
+ │ ├── test_files.py
198
+ │ ├── test_fmt.py
199
+ │ ├── test_html.py
200
+ │ ├── test_lint.py
201
+ │ ├── test_schedule.py
202
+ │ ├── test_security.py
203
+ │ ├── test_sql.py
204
+ │ ├── test_validate.py
205
+ │ ├── test_warnings.py
206
+ │ └── test_xml_xpath.py
207
+ ├── .gitignore
208
+ ├── .gitleaks.toml
209
+ ├── .pre-commit-config.yaml
210
+ ├── .secrets.baseline
211
+ ├── LICENSE
212
+ ├── pyproject.toml
213
+ └── README.md
214
+ ```
215
+
216
+ </details>
217
+
218
+ <!-- pinky:readme-docs:end -->
219
+
220
+ ______________________________________________________________________
221
+
222
+ ## License
223
+
224
+ <!-- pinky:readme-license:start -->
225
+
226
+ MIT — see [LICENSE](LICENSE).
227
+
228
+ <!-- pinky:readme-license:end -->