pam-python 0.2.0__tar.gz → 0.2.2__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.
- {pam_python-0.2.0/pam_python.egg-info → pam_python-0.2.2}/PKG-INFO +56 -60
- {pam_python-0.2.0 → pam_python-0.2.2}/README.md +50 -37
- {pam_python-0.2.0 → pam_python-0.2.2}/pam/__init__.py +1 -1
- pam_python-0.2.2/pam/cli.py +429 -0
- {pam_python-0.2.0 → pam_python-0.2.2}/pam/models/request_command.py +4 -0
- pam_python-0.2.2/pam/request_file_format.py +14 -0
- pam_python-0.2.2/pam/result_batch_uploader.py +227 -0
- {pam_python-0.2.0 → pam_python-0.2.2}/pam/server.py +22 -2
- {pam_python-0.2.0 → pam_python-0.2.2}/pam/service.py +38 -25
- {pam_python-0.2.0 → pam_python-0.2.2}/pam/templates/init/AGENT.md +48 -68
- {pam_python-0.2.0 → pam_python-0.2.2}/pam/templates/init/REPORTS.md +2 -2
- pam_python-0.2.2/pam/templates/init/dockerignore.tmpl +6 -0
- pam_python-0.2.2/pam/templates/project/pip/Dockerfile +16 -0
- {pam_python-0.2.0/pam/templates/init → pam_python-0.2.2/pam/templates/project/pip}/requirements.txt +0 -2
- pam_python-0.2.2/pam/templates/project/uv/Dockerfile +20 -0
- pam_python-0.2.2/pam/templates/project/uv/pyproject.toml +12 -0
- pam_python-0.2.2/pam/templates/project/uv/python-version +1 -0
- pam_python-0.2.2/pam/templates/service/pandas/functions.tmpl +13 -0
- {pam_python-0.2.0/pam/templates/service → pam_python-0.2.2/pam/templates/service/pandas}/service.test.tmpl +31 -46
- pam_python-0.2.2/pam/templates/service/pandas/service_class.tmpl +65 -0
- pam_python-0.2.2/pam/templates/service/polars/functions.tmpl +11 -0
- pam_python-0.2.2/pam/templates/service/polars/service.test.tmpl +122 -0
- pam_python-0.2.2/pam/templates/service/polars/service_class.tmpl +59 -0
- {pam_python-0.2.0 → pam_python-0.2.2}/pam/tester_task.py +22 -7
- {pam_python-0.2.0 → pam_python-0.2.2}/pam/utils.py +2 -4
- {pam_python-0.2.0 → pam_python-0.2.2/pam_python.egg-info}/PKG-INFO +56 -60
- {pam_python-0.2.0 → pam_python-0.2.2}/pam_python.egg-info/SOURCES.txt +14 -7
- pam_python-0.2.2/pam_python.egg-info/requires.txt +3 -0
- pam_python-0.2.2/pyproject.toml +54 -0
- pam_python-0.2.2/tests/test_cli_templates.py +185 -0
- {pam_python-0.2.0 → pam_python-0.2.2}/tests/test_result_batch_uploader.py +28 -2
- pam_python-0.2.2/tests/test_service_dataframe_output.py +41 -0
- {pam_python-0.2.0 → pam_python-0.2.2}/tests/test_sqlite_download_v2.py +21 -0
- pam_python-0.2.0/pam/cli.py +0 -226
- pam_python-0.2.0/pam/request_file_format.py +0 -5
- pam_python-0.2.0/pam/result_batch_uploader.py +0 -179
- pam_python-0.2.0/pam/templates/docker/Dockerfile +0 -21
- pam_python-0.2.0/pam/templates/init/dockerignore.tmpl +0 -4
- pam_python-0.2.0/pam/templates/service/functions.tmpl +0 -22
- pam_python-0.2.0/pam/templates/service/service_class.tmpl +0 -81
- pam_python-0.2.0/pam_python.egg-info/requires.txt +0 -9
- pam_python-0.2.0/setup.py +0 -43
- pam_python-0.2.0/tests/test_cli_templates.py +0 -78
- {pam_python-0.2.0 → pam_python-0.2.2}/LICENSE.txt +0 -0
- {pam_python-0.2.0 → pam_python-0.2.2}/pam/api.py +0 -0
- {pam_python-0.2.0 → pam_python-0.2.2}/pam/interface_task_manager.py +0 -0
- {pam_python-0.2.0 → pam_python-0.2.2}/pam/logger.py +0 -0
- {pam_python-0.2.0 → pam_python-0.2.2}/pam/models/__init__.py +0 -0
- {pam_python-0.2.0 → pam_python-0.2.2}/pam/models/sqlite_download.py +0 -0
- {pam_python-0.2.0 → pam_python-0.2.2}/pam/reports/__init__.py +0 -0
- {pam_python-0.2.0 → pam_python-0.2.2}/pam/reports/area.py +0 -0
- {pam_python-0.2.0 → pam_python-0.2.2}/pam/reports/axis.py +0 -0
- {pam_python-0.2.0 → pam_python-0.2.2}/pam/reports/bar.py +0 -0
- {pam_python-0.2.0 → pam_python-0.2.2}/pam/reports/box_plot.py +0 -0
- {pam_python-0.2.0 → pam_python-0.2.2}/pam/reports/bubble.py +0 -0
- {pam_python-0.2.0 → pam_python-0.2.2}/pam/reports/candlestick.py +0 -0
- {pam_python-0.2.0 → pam_python-0.2.2}/pam/reports/collection.py +0 -0
- {pam_python-0.2.0 → pam_python-0.2.2}/pam/reports/definition.py +0 -0
- {pam_python-0.2.0 → pam_python-0.2.2}/pam/reports/donut.py +0 -0
- {pam_python-0.2.0 → pam_python-0.2.2}/pam/reports/errors.py +0 -0
- {pam_python-0.2.0 → pam_python-0.2.2}/pam/reports/fields.py +0 -0
- {pam_python-0.2.0 → pam_python-0.2.2}/pam/reports/heatmap.py +0 -0
- {pam_python-0.2.0 → pam_python-0.2.2}/pam/reports/line.py +0 -0
- {pam_python-0.2.0 → pam_python-0.2.2}/pam/reports/manager.py +0 -0
- {pam_python-0.2.0 → pam_python-0.2.2}/pam/reports/metric.py +0 -0
- {pam_python-0.2.0 → pam_python-0.2.2}/pam/reports/pie.py +0 -0
- {pam_python-0.2.0 → pam_python-0.2.2}/pam/reports/polar_area.py +0 -0
- {pam_python-0.2.0 → pam_python-0.2.2}/pam/reports/radar.py +0 -0
- {pam_python-0.2.0 → pam_python-0.2.2}/pam/reports/radial_bar.py +0 -0
- {pam_python-0.2.0 → pam_python-0.2.2}/pam/reports/range_area.py +0 -0
- {pam_python-0.2.0 → pam_python-0.2.2}/pam/reports/range_bar.py +0 -0
- {pam_python-0.2.0 → pam_python-0.2.2}/pam/reports/range_transform.py +0 -0
- {pam_python-0.2.0 → pam_python-0.2.2}/pam/reports/record.py +0 -0
- {pam_python-0.2.0 → pam_python-0.2.2}/pam/reports/report.py +0 -0
- {pam_python-0.2.0 → pam_python-0.2.2}/pam/reports/scatter.py +0 -0
- {pam_python-0.2.0 → pam_python-0.2.2}/pam/reports/schema.py +0 -0
- {pam_python-0.2.0 → pam_python-0.2.2}/pam/reports/store.py +0 -0
- {pam_python-0.2.0 → pam_python-0.2.2}/pam/reports/table.py +0 -0
- {pam_python-0.2.0 → pam_python-0.2.2}/pam/reports/transport.py +0 -0
- {pam_python-0.2.0 → pam_python-0.2.2}/pam/reports/treemap.py +0 -0
- {pam_python-0.2.0 → pam_python-0.2.2}/pam/sqlite.py +0 -0
- {pam_python-0.2.0 → pam_python-0.2.2}/pam/task_manager.py +0 -0
- {pam_python-0.2.0 → pam_python-0.2.2}/pam/temp_file_utils.py +0 -0
- {pam_python-0.2.0 → pam_python-0.2.2}/pam/templates/buildcmd/pamb +0 -0
- {pam_python-0.2.0 → pam_python-0.2.2}/pam/templates/buildcmd/pamb-base.sh +0 -0
- {pam_python-0.2.0 → pam_python-0.2.2}/pam/templates/init/gitignore.tmpl +0 -0
- {pam_python-0.2.0 → pam_python-0.2.2}/pam/templates/init/main.tmpl +0 -0
- {pam_python-0.2.0 → pam_python-0.2.2}/pam/templates/init/pylintrc.tmpl +0 -0
- {pam_python-0.2.0 → pam_python-0.2.2}/pam/templates/init/run_unit_test.bat +0 -0
- {pam_python-0.2.0 → pam_python-0.2.2}/pam/templates/init/run_unit_test.ps1 +0 -0
- {pam_python-0.2.0 → pam_python-0.2.2}/pam/templates/init/run_unit_test.sh +0 -0
- {pam_python-0.2.0/pam/templates/service → pam_python-0.2.2/pam/templates/service/common}/service.yaml +1 -1
- {pam_python-0.2.0 → pam_python-0.2.2}/pam_python.egg-info/dependency_links.txt +0 -0
- {pam_python-0.2.0 → pam_python-0.2.2}/pam_python.egg-info/entry_points.txt +0 -0
- {pam_python-0.2.0 → pam_python-0.2.2}/pam_python.egg-info/top_level.txt +0 -0
- {pam_python-0.2.0 → pam_python-0.2.2}/setup.cfg +0 -0
- {pam_python-0.2.0 → pam_python-0.2.2}/tests/test_range_transform.py +0 -0
- {pam_python-0.2.0 → pam_python-0.2.2}/tests/test_report_store.py +0 -0
- {pam_python-0.2.0 → pam_python-0.2.2}/tests/test_typed_area_report.py +0 -0
- {pam_python-0.2.0 → pam_python-0.2.2}/tests/test_typed_bar_report.py +0 -0
- {pam_python-0.2.0 → pam_python-0.2.2}/tests/test_typed_box_plot_report.py +0 -0
- {pam_python-0.2.0 → pam_python-0.2.2}/tests/test_typed_bubble_report.py +0 -0
- {pam_python-0.2.0 → pam_python-0.2.2}/tests/test_typed_candlestick_report.py +0 -0
- {pam_python-0.2.0 → pam_python-0.2.2}/tests/test_typed_donut_report.py +0 -0
- {pam_python-0.2.0 → pam_python-0.2.2}/tests/test_typed_heatmap_report.py +0 -0
- {pam_python-0.2.0 → pam_python-0.2.2}/tests/test_typed_line_report.py +0 -0
- {pam_python-0.2.0 → pam_python-0.2.2}/tests/test_typed_metric_report.py +0 -0
- {pam_python-0.2.0 → pam_python-0.2.2}/tests/test_typed_pie_report.py +0 -0
- {pam_python-0.2.0 → pam_python-0.2.2}/tests/test_typed_polar_area_report.py +0 -0
- {pam_python-0.2.0 → pam_python-0.2.2}/tests/test_typed_radar_report.py +0 -0
- {pam_python-0.2.0 → pam_python-0.2.2}/tests/test_typed_radial_bar_report.py +0 -0
- {pam_python-0.2.0 → pam_python-0.2.2}/tests/test_typed_range_area_report.py +0 -0
- {pam_python-0.2.0 → pam_python-0.2.2}/tests/test_typed_range_bar_report.py +0 -0
- {pam_python-0.2.0 → pam_python-0.2.2}/tests/test_typed_scatter_report.py +0 -0
- {pam_python-0.2.0 → pam_python-0.2.2}/tests/test_typed_table_report.py +0 -0
- {pam_python-0.2.0 → pam_python-0.2.2}/tests/test_typed_treemap_report.py +0 -0
|
@@ -1,39 +1,22 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: pam-python
|
|
3
|
-
Version: 0.2.
|
|
3
|
+
Version: 0.2.2
|
|
4
4
|
Summary: Pam Python Library
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
Author-email: narongrit@pams.ai
|
|
5
|
+
Author-email: Narongrit Kanhanoi <narongrit@pams.ai>
|
|
6
|
+
Project-URL: Homepage, https://github.com/heart/pam-python
|
|
8
7
|
Classifier: Programming Language :: Python :: 3
|
|
9
|
-
Classifier: Programming Language :: Python :: 3.
|
|
10
|
-
Classifier: License :: Other/Proprietary License
|
|
8
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
11
9
|
Classifier: Operating System :: OS Independent
|
|
12
10
|
Classifier: Intended Audience :: Developers
|
|
13
11
|
Classifier: Intended Audience :: Customer Service
|
|
14
12
|
Classifier: Topic :: Software Development :: Libraries
|
|
15
|
-
Requires-Python:
|
|
13
|
+
Requires-Python: <3.13,>=3.12
|
|
16
14
|
Description-Content-Type: text/markdown
|
|
17
15
|
License-File: LICENSE.txt
|
|
18
|
-
Requires-Dist: setuptools>=70.0.0
|
|
19
16
|
Requires-Dist: Flask>=3.0.2
|
|
20
|
-
Requires-Dist: aiohttp>=3.11.11
|
|
21
|
-
Requires-Dist: pandas>=2.2.3
|
|
22
|
-
Requires-Dist: dask>=2024.12.1
|
|
23
|
-
Requires-Dist: dask-expr>=1.1.21
|
|
24
17
|
Requires-Dist: requests>=2.32.3
|
|
25
|
-
Requires-Dist:
|
|
26
|
-
Requires-Dist: pyarrow>=19.0.1
|
|
27
|
-
Dynamic: author
|
|
28
|
-
Dynamic: author-email
|
|
29
|
-
Dynamic: classifier
|
|
30
|
-
Dynamic: description
|
|
31
|
-
Dynamic: description-content-type
|
|
32
|
-
Dynamic: home-page
|
|
18
|
+
Requires-Dist: PyYAML>=6.0.2
|
|
33
19
|
Dynamic: license-file
|
|
34
|
-
Dynamic: requires-dist
|
|
35
|
-
Dynamic: requires-python
|
|
36
|
-
Dynamic: summary
|
|
37
20
|
|
|
38
21
|
# pam-python-data-plugin-framework
|
|
39
22
|
|
|
@@ -41,8 +24,6 @@ This repository provides the `pam` CLI and runtime framework to build Data Plugi
|
|
|
41
24
|
|
|
42
25
|
This README is a practical, step-by-step guide you can follow to create and run a real service.
|
|
43
26
|
|
|
44
|
-
Current release target: **0.2.0**
|
|
45
|
-
|
|
46
27
|
**What you get**
|
|
47
28
|
|
|
48
29
|
- CLI to initialize a project and scaffold services
|
|
@@ -72,54 +53,54 @@ Current release target: **0.2.0**
|
|
|
72
53
|
|
|
73
54
|
**Prerequisites**
|
|
74
55
|
|
|
75
|
-
- Python 3.
|
|
76
|
-
- `
|
|
56
|
+
- Python 3.12
|
|
57
|
+
- `uv` (recommended) or `pip`
|
|
77
58
|
|
|
78
59
|
---
|
|
79
60
|
|
|
80
|
-
**
|
|
81
|
-
|
|
61
|
+
**Create a uv project**
|
|
62
|
+
|
|
63
|
+
Install PAM inside each project instead of installing the CLI globally:
|
|
82
64
|
|
|
83
65
|
```bash
|
|
84
66
|
mkdir my_data_plugin
|
|
85
67
|
cd my_data_plugin
|
|
86
|
-
|
|
87
|
-
|
|
68
|
+
uv init --python 3.12
|
|
69
|
+
uv add pam-python
|
|
70
|
+
uv run pam init
|
|
88
71
|
```
|
|
89
72
|
|
|
90
|
-
|
|
73
|
+
`pam init` recognizes a fresh uv project, keeps its uv configuration, and adds
|
|
74
|
+
the PAM scaffold. It refuses projects whose generated source files have already
|
|
75
|
+
been edited.
|
|
91
76
|
|
|
92
|
-
|
|
93
|
-
pip install pam-python
|
|
94
|
-
```
|
|
95
|
-
|
|
96
|
-
---
|
|
97
|
-
|
|
98
|
-
**Initialize a Project**
|
|
99
|
-
This creates a runnable project with templates, including `AGENT.md` for runtime
|
|
100
|
-
rules and `REPORTS.md` for the managed-report public API and copyable examples.
|
|
77
|
+
Pip remains supported for new projects that do not use uv:
|
|
101
78
|
|
|
102
79
|
```bash
|
|
80
|
+
mkdir my_data_plugin
|
|
81
|
+
cd my_data_plugin
|
|
82
|
+
python3 -m venv venv
|
|
83
|
+
source venv/bin/activate
|
|
84
|
+
python -m pip install pam-python
|
|
103
85
|
pam init
|
|
104
86
|
```
|
|
105
87
|
|
|
106
|
-
When
|
|
107
|
-
|
|
108
|
-
- overwrite
|
|
109
|
-
- keep
|
|
110
|
-
- merge
|
|
111
|
-
|
|
112
|
-
`pam init` now writes a project baseline `requirements.txt`. It pins `pam-python` to the scaffold version and includes core runtime/test dependencies used by the generated project. It does not copy the current machine's `pip freeze`.
|
|
88
|
+
When run in an empty directory, `pam init` still asks whether to create a uv or
|
|
89
|
+
pip project. PAM never migrates an existing application.
|
|
113
90
|
|
|
114
91
|
---
|
|
115
92
|
|
|
116
93
|
**Create a Service**
|
|
117
|
-
Generate a service scaffold. Do not hand-create service templates.
|
|
118
94
|
|
|
119
95
|
```bash
|
|
120
|
-
pam new service rfm_segment
|
|
96
|
+
uv run pam new service rfm_segment
|
|
121
97
|
```
|
|
122
98
|
|
|
99
|
+
The generator asks you to select Polars (default) or Pandas. Polars services use
|
|
100
|
+
lazy Parquet processing. Pandas services also receive Parquet and install
|
|
101
|
+
`pandas` plus `pyarrow`. The selected direct dependencies are added to the
|
|
102
|
+
project automatically.
|
|
103
|
+
|
|
123
104
|
This creates a new folder (e.g. `rfm_segment/`) with:
|
|
124
105
|
|
|
125
106
|
- a service class (`RfmSegmentSvc.py`)
|
|
@@ -141,7 +122,8 @@ The runtime calls your service in two main phases.
|
|
|
141
122
|
2. `on_data_input`
|
|
142
123
|
|
|
143
124
|
- Called when CDP sends input files
|
|
144
|
-
- `req.input_files` contains ordered
|
|
125
|
+
- `req.input_files` contains ordered Parquet files
|
|
126
|
+
- `req.file_format` is the typed format sent by PAM
|
|
145
127
|
- Should also return quickly (use a thread if needed)
|
|
146
128
|
|
|
147
129
|
When your service is done:
|
|
@@ -169,7 +151,11 @@ Notes:
|
|
|
169
151
|
---
|
|
170
152
|
|
|
171
153
|
**Uploading Results in Batches**
|
|
172
|
-
|
|
154
|
+
Polars templates pass `LazyFrame` results directly to `_upload_result(...)`, which
|
|
155
|
+
streams them to the CSV upload boundary. Do not collect or convert to Pandas first.
|
|
156
|
+
|
|
157
|
+
For eager Polars or Pandas DataFrames, use the batch uploader to handle chunking
|
|
158
|
+
and flushing automatically.
|
|
173
159
|
|
|
174
160
|
Recommended usage:
|
|
175
161
|
|
|
@@ -189,6 +175,7 @@ Notes:
|
|
|
189
175
|
- `options` may be passed to `upload(...)` and are forwarded to `_upload_result`.
|
|
190
176
|
- Complete batches upload immediately; `flush()` sends the remaining rows.
|
|
191
177
|
- `get_status()` returns buffered rows, uploaded rows, and uploaded batch counts per stream.
|
|
178
|
+
- `ResultBatchUploader` intentionally rejects Polars `LazyFrame`; upload it directly.
|
|
192
179
|
|
|
193
180
|
---
|
|
194
181
|
|
|
@@ -225,7 +212,8 @@ Important rules:
|
|
|
225
212
|
The generated `main.py` runs the Flask server.
|
|
226
213
|
|
|
227
214
|
```bash
|
|
228
|
-
python main.py
|
|
215
|
+
uv run python main.py # uv project
|
|
216
|
+
python main.py # pip project
|
|
229
217
|
```
|
|
230
218
|
|
|
231
219
|
By default it binds to `0.0.0.0:8000`. You can override with:
|
|
@@ -238,9 +226,13 @@ export SERVER_PORT=8000
|
|
|
238
226
|
---
|
|
239
227
|
|
|
240
228
|
**Testing a Service**
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
229
|
+
For uv projects, use one cross-platform command:
|
|
230
|
+
|
|
231
|
+
```bash
|
|
232
|
+
uv run python -m unittest discover -s rfm_segment -p "test_*.py"
|
|
233
|
+
```
|
|
234
|
+
|
|
235
|
+
Pip projects include shell-specific scripts:
|
|
244
236
|
|
|
245
237
|
macOS/Linux:
|
|
246
238
|
|
|
@@ -285,20 +277,23 @@ After `pam init` and one service:
|
|
|
285
277
|
├── AGENT.md
|
|
286
278
|
├── REPORTS.md
|
|
287
279
|
├── Dockerfile
|
|
280
|
+
├── pyproject.toml
|
|
281
|
+
├── uv.lock
|
|
282
|
+
├── .python-version
|
|
288
283
|
├── rfm_segment/
|
|
289
284
|
│ ├── RfmSegmentSvc.py
|
|
290
285
|
│ ├── functions.py
|
|
291
286
|
│ ├── service.yaml
|
|
292
287
|
│ └── test_rfm_segment.py
|
|
293
|
-
|
|
294
|
-
└── run_unit_test.sh
|
|
288
|
+
└── .pam-project
|
|
295
289
|
```
|
|
296
290
|
|
|
297
291
|
---
|
|
298
292
|
|
|
299
293
|
**Troubleshooting**
|
|
300
294
|
|
|
301
|
-
- If `pam`
|
|
295
|
+
- If `pam` is missing, verify the uv tool installation or activate the pip virtualenv.
|
|
296
|
+
- If uv initialization fails, verify `uv` is on `PATH` and package indexes are reachable.
|
|
302
297
|
- If `pam new service` fails, confirm the service name is provided.
|
|
303
298
|
- If temp cleanup is too frequent or too slow, adjust `TEMP_CLEAN_INTERVAL_HOURS` and `TEMP_CLEAN_DAYS`.
|
|
304
299
|
|
|
@@ -309,5 +304,6 @@ After `pam init` and one service:
|
|
|
309
304
|
- Implement your logic in `functions.py`.
|
|
310
305
|
- Wire it into `on_start` and `on_data_input` in your service class.
|
|
311
306
|
- Use the temp utilities to write intermediate files.
|
|
312
|
-
-
|
|
307
|
+
- Keep Polars transformations lazy and upload the resulting `LazyFrame` directly.
|
|
308
|
+
- Use `ResultBatchUploader` only for eager Polars/Pandas frames that need row batching.
|
|
313
309
|
- Read `REPORTS.md` before implementing a managed report.
|
|
@@ -4,8 +4,6 @@ This repository provides the `pam` CLI and runtime framework to build Data Plugi
|
|
|
4
4
|
|
|
5
5
|
This README is a practical, step-by-step guide you can follow to create and run a real service.
|
|
6
6
|
|
|
7
|
-
Current release target: **0.2.0**
|
|
8
|
-
|
|
9
7
|
**What you get**
|
|
10
8
|
|
|
11
9
|
- CLI to initialize a project and scaffold services
|
|
@@ -35,54 +33,54 @@ Current release target: **0.2.0**
|
|
|
35
33
|
|
|
36
34
|
**Prerequisites**
|
|
37
35
|
|
|
38
|
-
- Python 3.
|
|
39
|
-
- `
|
|
36
|
+
- Python 3.12
|
|
37
|
+
- `uv` (recommended) or `pip`
|
|
40
38
|
|
|
41
39
|
---
|
|
42
40
|
|
|
43
|
-
**
|
|
44
|
-
|
|
41
|
+
**Create a uv project**
|
|
42
|
+
|
|
43
|
+
Install PAM inside each project instead of installing the CLI globally:
|
|
45
44
|
|
|
46
45
|
```bash
|
|
47
46
|
mkdir my_data_plugin
|
|
48
47
|
cd my_data_plugin
|
|
49
|
-
|
|
50
|
-
|
|
48
|
+
uv init --python 3.12
|
|
49
|
+
uv add pam-python
|
|
50
|
+
uv run pam init
|
|
51
51
|
```
|
|
52
52
|
|
|
53
|
-
|
|
53
|
+
`pam init` recognizes a fresh uv project, keeps its uv configuration, and adds
|
|
54
|
+
the PAM scaffold. It refuses projects whose generated source files have already
|
|
55
|
+
been edited.
|
|
54
56
|
|
|
55
|
-
|
|
56
|
-
pip install pam-python
|
|
57
|
-
```
|
|
58
|
-
|
|
59
|
-
---
|
|
60
|
-
|
|
61
|
-
**Initialize a Project**
|
|
62
|
-
This creates a runnable project with templates, including `AGENT.md` for runtime
|
|
63
|
-
rules and `REPORTS.md` for the managed-report public API and copyable examples.
|
|
57
|
+
Pip remains supported for new projects that do not use uv:
|
|
64
58
|
|
|
65
59
|
```bash
|
|
60
|
+
mkdir my_data_plugin
|
|
61
|
+
cd my_data_plugin
|
|
62
|
+
python3 -m venv venv
|
|
63
|
+
source venv/bin/activate
|
|
64
|
+
python -m pip install pam-python
|
|
66
65
|
pam init
|
|
67
66
|
```
|
|
68
67
|
|
|
69
|
-
When
|
|
70
|
-
|
|
71
|
-
- overwrite
|
|
72
|
-
- keep
|
|
73
|
-
- merge
|
|
74
|
-
|
|
75
|
-
`pam init` now writes a project baseline `requirements.txt`. It pins `pam-python` to the scaffold version and includes core runtime/test dependencies used by the generated project. It does not copy the current machine's `pip freeze`.
|
|
68
|
+
When run in an empty directory, `pam init` still asks whether to create a uv or
|
|
69
|
+
pip project. PAM never migrates an existing application.
|
|
76
70
|
|
|
77
71
|
---
|
|
78
72
|
|
|
79
73
|
**Create a Service**
|
|
80
|
-
Generate a service scaffold. Do not hand-create service templates.
|
|
81
74
|
|
|
82
75
|
```bash
|
|
83
|
-
pam new service rfm_segment
|
|
76
|
+
uv run pam new service rfm_segment
|
|
84
77
|
```
|
|
85
78
|
|
|
79
|
+
The generator asks you to select Polars (default) or Pandas. Polars services use
|
|
80
|
+
lazy Parquet processing. Pandas services also receive Parquet and install
|
|
81
|
+
`pandas` plus `pyarrow`. The selected direct dependencies are added to the
|
|
82
|
+
project automatically.
|
|
83
|
+
|
|
86
84
|
This creates a new folder (e.g. `rfm_segment/`) with:
|
|
87
85
|
|
|
88
86
|
- a service class (`RfmSegmentSvc.py`)
|
|
@@ -104,7 +102,8 @@ The runtime calls your service in two main phases.
|
|
|
104
102
|
2. `on_data_input`
|
|
105
103
|
|
|
106
104
|
- Called when CDP sends input files
|
|
107
|
-
- `req.input_files` contains ordered
|
|
105
|
+
- `req.input_files` contains ordered Parquet files
|
|
106
|
+
- `req.file_format` is the typed format sent by PAM
|
|
108
107
|
- Should also return quickly (use a thread if needed)
|
|
109
108
|
|
|
110
109
|
When your service is done:
|
|
@@ -132,7 +131,11 @@ Notes:
|
|
|
132
131
|
---
|
|
133
132
|
|
|
134
133
|
**Uploading Results in Batches**
|
|
135
|
-
|
|
134
|
+
Polars templates pass `LazyFrame` results directly to `_upload_result(...)`, which
|
|
135
|
+
streams them to the CSV upload boundary. Do not collect or convert to Pandas first.
|
|
136
|
+
|
|
137
|
+
For eager Polars or Pandas DataFrames, use the batch uploader to handle chunking
|
|
138
|
+
and flushing automatically.
|
|
136
139
|
|
|
137
140
|
Recommended usage:
|
|
138
141
|
|
|
@@ -152,6 +155,7 @@ Notes:
|
|
|
152
155
|
- `options` may be passed to `upload(...)` and are forwarded to `_upload_result`.
|
|
153
156
|
- Complete batches upload immediately; `flush()` sends the remaining rows.
|
|
154
157
|
- `get_status()` returns buffered rows, uploaded rows, and uploaded batch counts per stream.
|
|
158
|
+
- `ResultBatchUploader` intentionally rejects Polars `LazyFrame`; upload it directly.
|
|
155
159
|
|
|
156
160
|
---
|
|
157
161
|
|
|
@@ -188,7 +192,8 @@ Important rules:
|
|
|
188
192
|
The generated `main.py` runs the Flask server.
|
|
189
193
|
|
|
190
194
|
```bash
|
|
191
|
-
python main.py
|
|
195
|
+
uv run python main.py # uv project
|
|
196
|
+
python main.py # pip project
|
|
192
197
|
```
|
|
193
198
|
|
|
194
199
|
By default it binds to `0.0.0.0:8000`. You can override with:
|
|
@@ -201,9 +206,13 @@ export SERVER_PORT=8000
|
|
|
201
206
|
---
|
|
202
207
|
|
|
203
208
|
**Testing a Service**
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
209
|
+
For uv projects, use one cross-platform command:
|
|
210
|
+
|
|
211
|
+
```bash
|
|
212
|
+
uv run python -m unittest discover -s rfm_segment -p "test_*.py"
|
|
213
|
+
```
|
|
214
|
+
|
|
215
|
+
Pip projects include shell-specific scripts:
|
|
207
216
|
|
|
208
217
|
macOS/Linux:
|
|
209
218
|
|
|
@@ -248,20 +257,23 @@ After `pam init` and one service:
|
|
|
248
257
|
├── AGENT.md
|
|
249
258
|
├── REPORTS.md
|
|
250
259
|
├── Dockerfile
|
|
260
|
+
├── pyproject.toml
|
|
261
|
+
├── uv.lock
|
|
262
|
+
├── .python-version
|
|
251
263
|
├── rfm_segment/
|
|
252
264
|
│ ├── RfmSegmentSvc.py
|
|
253
265
|
│ ├── functions.py
|
|
254
266
|
│ ├── service.yaml
|
|
255
267
|
│ └── test_rfm_segment.py
|
|
256
|
-
|
|
257
|
-
└── run_unit_test.sh
|
|
268
|
+
└── .pam-project
|
|
258
269
|
```
|
|
259
270
|
|
|
260
271
|
---
|
|
261
272
|
|
|
262
273
|
**Troubleshooting**
|
|
263
274
|
|
|
264
|
-
- If `pam`
|
|
275
|
+
- If `pam` is missing, verify the uv tool installation or activate the pip virtualenv.
|
|
276
|
+
- If uv initialization fails, verify `uv` is on `PATH` and package indexes are reachable.
|
|
265
277
|
- If `pam new service` fails, confirm the service name is provided.
|
|
266
278
|
- If temp cleanup is too frequent or too slow, adjust `TEMP_CLEAN_INTERVAL_HOURS` and `TEMP_CLEAN_DAYS`.
|
|
267
279
|
|
|
@@ -272,5 +284,6 @@ After `pam init` and one service:
|
|
|
272
284
|
- Implement your logic in `functions.py`.
|
|
273
285
|
- Wire it into `on_start` and `on_data_input` in your service class.
|
|
274
286
|
- Use the temp utilities to write intermediate files.
|
|
275
|
-
-
|
|
287
|
+
- Keep Polars transformations lazy and upload the resulting `LazyFrame` directly.
|
|
288
|
+
- Use `ResultBatchUploader` only for eager Polars/Pandas frames that need row batching.
|
|
276
289
|
- Read `REPORTS.md` before implementing a managed report.
|