pam-python 0.2.7__py3-none-any.whl → 0.2.9__py3-none-any.whl

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/__init__.py CHANGED
@@ -1,3 +1,3 @@
1
1
  """PAM Data Plugin framework."""
2
2
 
3
- __version__ = "0.2.7"
3
+ __version__ = "0.2.9"
pam/cli.py CHANGED
@@ -59,6 +59,8 @@ ENGINE_DEPENDENCIES = {
59
59
  "pandas": ["pandas>=2.2.3", "pyarrow>=19.0.1"],
60
60
  }
61
61
 
62
+ UV_RUNTIME_DEPENDENCIES = ["Flask>=3.0.2", "gunicorn>=23.0.0"]
63
+
62
64
 
63
65
  def main(argv=None):
64
66
  args = list(sys.argv[1:] if argv is None else argv)
@@ -238,6 +240,9 @@ def init_project(package_manager: str | None = None):
238
240
  if not _prompt_overwrite_if_needed(destination, templates):
239
241
  return
240
242
 
243
+ if preinitialized_uv:
244
+ _run_uv(["add", *UV_RUNTIME_DEPENDENCIES], destination)
245
+
241
246
  with tempfile.TemporaryDirectory(prefix=".pam-init-", dir=destination) as temp_dir:
242
247
  staging = Path(temp_dir)
243
248
  for src, dest in templates.items():
@@ -2,7 +2,7 @@ from __future__ import annotations
2
2
 
3
3
  import csv
4
4
  from dataclasses import dataclass
5
- from datetime import datetime
5
+ from datetime import datetime, timezone
6
6
  from hashlib import sha256
7
7
  import json
8
8
  from pathlib import Path
@@ -15,6 +15,7 @@ V2_PROTOCOL_VERSION = "data-plugin-input/v2"
15
15
  V2_MANIFEST_FILE_NAME = "manifest.json"
16
16
  V2_MAX_DATASETS = 5
17
17
  V2_POPULATION_MODES = {"contacts", "event_any", "event_all"}
18
+ PAM_TIMESTAMP_FORMAT = "%Y-%m-%d %H:%M:%S"
18
19
 
19
20
 
20
21
  @dataclass(frozen=True)
@@ -364,6 +365,11 @@ def _required_optional_string(source: dict[str, Any], key: str) -> str:
364
365
  return value
365
366
 
366
367
 
368
+ def _parse_pam_timestamp(value: str) -> datetime:
369
+ """Parse PAM's suffix-free timestamp as UTC+0 without shifting it."""
370
+ return datetime.strptime(value, PAM_TIMESTAMP_FORMAT).replace(tzinfo=timezone.utc)
371
+
372
+
367
373
  def _parse_time_window(raw_time_window: Any) -> DataInputV2TimeWindow | None:
368
374
  if raw_time_window is None:
369
375
  return None
@@ -374,11 +380,11 @@ def _parse_time_window(raw_time_window: Any) -> DataInputV2TimeWindow | None:
374
380
  start_inclusive = _required_bool(raw_time_window, "start_inclusive")
375
381
  end_inclusive = _required_bool(raw_time_window, "end_inclusive")
376
382
  try:
377
- start_time = datetime.fromisoformat(start.replace("Z", "+00:00"))
378
- end_time = datetime.fromisoformat(end.replace("Z", "+00:00"))
383
+ start_time = _parse_pam_timestamp(start)
384
+ end_time = _parse_pam_timestamp(end)
379
385
  except ValueError as error:
380
386
  raise ValueError("V2 manifest time_window timestamps are invalid") from error
381
- if start_time.tzinfo is None or end_time.tzinfo is None or start_time >= end_time:
387
+ if start_time >= end_time:
382
388
  raise ValueError("V2 manifest time_window order is invalid")
383
389
  if not start_inclusive or end_inclusive:
384
390
  raise ValueError("V2 manifest time_window must be half-open")
@@ -1,5 +1,13 @@
1
1
  # AGENT
2
2
 
3
+ ## PAM date and time contract
4
+
5
+ PAM timestamps are UTC+0 strings in the exact format `%Y-%m-%d %H:%M:%S`, for
6
+ example `2026-03-31 02:37:44`. The timezone suffix is intentionally omitted.
7
+ Interpret these values as UTC without shifting the clock value. Do not emit or
8
+ expect RFC3339, ISO-8601, `T`, `Z`, or explicit timezone suffixes in internal PAM
9
+ contracts.
10
+
3
11
  This document defines the **data plugin runtime specification** and execution contract so developers and agents can create services and understand runtime behavior inside the CDP environment.
4
12
 
5
13
  This is not a tutorial.
@@ -17,4 +17,4 @@ RUN mkdir -p "$DATA_FOLDER"
17
17
  EXPOSE 8000
18
18
 
19
19
  # Keep one worker: service lifecycle state is local to the process.
20
- CMD ["gunicorn", "-w", "1", "-b", "0.0.0.0:8000", "main:app"]
20
+ CMD ["uv", "run", "--no-sync", "gunicorn", "-w", "1", "-b", "0.0.0.0:8000", "main:app"]
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: pam-python
3
- Version: 0.2.7
3
+ Version: 0.2.9
4
4
  Summary: Pam Python Library
5
5
  Author-email: Narongrit Kanhanoi <narongrit@pams.ai>
6
6
  Project-URL: Homepage, https://github.com/heart/pam-python
@@ -70,9 +70,9 @@ uv add pam-python
70
70
  uv run pam init
71
71
  ```
72
72
 
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.
73
+ `pam init` recognizes a fresh uv project, keeps its uv configuration, adds the
74
+ Flask and Gunicorn runtime dependencies, and adds the PAM scaffold. It refuses
75
+ projects whose generated source files have already been edited.
76
76
 
77
77
  Pip remains supported for new projects that do not use uv:
78
78
 
@@ -1,6 +1,6 @@
1
- pam/__init__.py,sha256=U3TQ5a4YxJeZTrlqKyKOvXPGWW7MMKw624l-8NBMCi4,56
1
+ pam/__init__.py,sha256=WEZJbeetmhwTurBIMc09FICFV6I2Hl6A8nMFuH6CaXI,56
2
2
  pam/api.py,sha256=T1plaZ1EyGFz59EdP5ibnl7n0NxduhBPN-jGYe20rCU,7844
3
- pam/cli.py,sha256=n1gPTP0TwT0rV-3nrYLjSDc4bL77Jogpreb1HazJ5YA,13633
3
+ pam/cli.py,sha256=fCtqVfMjmxNuJWzpwc51zHdjepyW10GT38u27QdUq3o,13788
4
4
  pam/interface_task_manager.py,sha256=V6WMifh5t0tIq9m46eH0RlXDmhbRNPc7XVs6R2rOejE,1985
5
5
  pam/logger.py,sha256=JYPfIpaQZy6QrfVKrzEMrxOgTzWIEhoegvIZgHTGWoc,3021
6
6
  pam/request_file_format.py,sha256=fSRlJagbvVGqdIEqhINSeqBNUTNQdm8GL5JfqgX9bxU,317
@@ -13,7 +13,7 @@ pam/temp_file_utils.py,sha256=noMyF4SZ17AWtZcRkZtNtdObRWzykAMxeFr-WkOEQi4,9450
13
13
  pam/tester_task.py,sha256=6hiCvNSWvx43tLGKSbiAyNTvlcp1E0QaJ_DH4LMUDZg,11156
14
14
  pam/utils.py,sha256=-pfnQ00Ib-IXvrMz4S5lTlO36PtSaJoQOhwXpwet8h4,1744
15
15
  pam/models/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
16
- pam/models/data_input_v2.py,sha256=hqREASPo2-xGUBAaV5jAFjBrcu-YMqg5tJlWfzhKmbs,16454
16
+ pam/models/data_input_v2.py,sha256=Ons3SqxMOcBQVtdjD9MgaZ8C_-2zhHhKG8A81RjQi_A,16614
17
17
  pam/models/request_command.py,sha256=6QBJyQfu75bMfhqBUILE1chvhjRdZ8WHWiBbX3k8K5s,14211
18
18
  pam/models/sqlite_download.py,sha256=7JMWwLp_5LSCMR5Ppk1RgbgcOaX_2LmRFrJiADAnIfs,678
19
19
  pam/reports/__init__.py,sha256=g6F1r52vyAOg9nGB2sTyKSCCla7E1IPwh7epHmaSBoI,2302
@@ -49,7 +49,7 @@ pam/reports/transport.py,sha256=t3oEVptS9msnNafia1G8vmTUCdhxvV5L2-0AY-8SKFk,2062
49
49
  pam/reports/treemap.py,sha256=W_VwxbiRMSvkNpY2z1HfbZtGKWXNr6FdbVdI0grVesw,4251
50
50
  pam/templates/buildcmd/pamb,sha256=58GCN59ysvHGNhPvTD3nkF4aLg-kM1WfWmH7CL3eQVo,530
51
51
  pam/templates/buildcmd/pamb-base.sh,sha256=Q0DkbeHtPx33PIjc68EeI7ZIwBOLlwcSmPhpxALXxVY,518
52
- pam/templates/init/AGENT.md,sha256=vH1LXCgnFet8Ks6zMb6PFTD_bUVPufAbw7vn5xY5wng,20593
52
+ pam/templates/init/AGENT.md,sha256=VxwD-aPfwcUAaZSt7JnhKpr7rJbuaCZ4KkJYrbOePNE,20952
53
53
  pam/templates/init/REPORTS.md,sha256=rg9oi0_xDYr0BZBluJh0vPF8lSx7mCRFpDCpUgNl68A,42851
54
54
  pam/templates/init/dockerignore.tmpl,sha256=Ht1eCgQLqalEwpUjih0FQavzhYJbT2jH-FP4btM3960,52
55
55
  pam/templates/init/gitignore.tmpl,sha256=xi8KBrPKSCPiuwtKP8xsF4shQbYowh1_LGW3fKbl09M,917
@@ -60,7 +60,7 @@ pam/templates/init/run_unit_test.ps1,sha256=4O9fciyt04j1XjwOJELPwGTv0XOd2CmZ1e7u
60
60
  pam/templates/init/run_unit_test.sh,sha256=4yVDKgI3bLK6CujRViF9UBOdNGoLFwcluZvmpYyi6kg,161
61
61
  pam/templates/project/pip/Dockerfile,sha256=VSfnPGRwuq-WoJ0ddfdnteJJM2eUlTJPdhTQfYhFWMw,382
62
62
  pam/templates/project/pip/requirements.txt,sha256=2hwbTfQsI_AEPWdsym2q0y5uTyO6Cekth_k2RZxYGx8,56
63
- pam/templates/project/uv/Dockerfile,sha256=TAvyq7NGwdn5RonrxPBYJzfAvS0rjBE3igjy1LzOnow,440
63
+ pam/templates/project/uv/Dockerfile,sha256=MTeMf2g8-gShxK7s4Iw5jvNN8uOklm8NVnK7jkS7T8M,466
64
64
  pam/templates/project/uv/pyproject.toml,sha256=30a2hJn6oN1AGvmgEBdxLETOBlHY8L-IIpIy8637yJM,208
65
65
  pam/templates/project/uv/python-version,sha256=e1X45ntWI8S-8_ppEojalDfXnTq6FW3kjUgdsyrH0W0,5
66
66
  pam/templates/service/common/service.yaml,sha256=ceyFswXjTh07vB4hrJEu7pj3mjlq3NPD4GPYTmvGwmE,44
@@ -70,9 +70,9 @@ pam/templates/service/pandas/service_class.tmpl,sha256=qaq9tqRhxogWQlqi09dbX5vLu
70
70
  pam/templates/service/polars/functions.tmpl,sha256=7Qlwsd5Q1vdkOvXHtlltjb2oS-i7JuNKPor2iNDSsQg,701
71
71
  pam/templates/service/polars/service.test.tmpl,sha256=nC6VoauZMYy_UTTXxxergfWbXvUD_VWwpM7-7A3aKqE,6881
72
72
  pam/templates/service/polars/service_class.tmpl,sha256=jMGeruXduu0eBB7FPDEkZu8b4mSIO3uRt5TiDuNq5T4,3688
73
- pam_python-0.2.7.dist-info/licenses/LICENSE.txt,sha256=yeHD2-gzrkkET2ZkMe22WyUGpDjlwTrqA4POb0FoZi4,1138
74
- pam_python-0.2.7.dist-info/METADATA,sha256=T2NuYuNt32F9WkIgVrWGXRNs6_Gg3GK0ZopEFymPimU,9974
75
- pam_python-0.2.7.dist-info/WHEEL,sha256=YVMoNqKzERt-wjUZwJ33xBGAwnFl-4cqbYkTtWa4itE,91
76
- pam_python-0.2.7.dist-info/entry_points.txt,sha256=xqYpsIqvNlO0j8elZc02bDwR-12dLK9FU2B1ACsLYxw,37
77
- pam_python-0.2.7.dist-info/top_level.txt,sha256=0EOjbyc3hQyzjhn6iyMgsEseqA66Xz0p27iBN7G7W1w,4
78
- pam_python-0.2.7.dist-info/RECORD,,
73
+ pam_python-0.2.9.dist-info/licenses/LICENSE.txt,sha256=yeHD2-gzrkkET2ZkMe22WyUGpDjlwTrqA4POb0FoZi4,1138
74
+ pam_python-0.2.9.dist-info/METADATA,sha256=Blclje6yaOK_mrU0q8jfgnw9rK8Altzgmm4qtmTXwLw,10024
75
+ pam_python-0.2.9.dist-info/WHEEL,sha256=YVMoNqKzERt-wjUZwJ33xBGAwnFl-4cqbYkTtWa4itE,91
76
+ pam_python-0.2.9.dist-info/entry_points.txt,sha256=xqYpsIqvNlO0j8elZc02bDwR-12dLK9FU2B1ACsLYxw,37
77
+ pam_python-0.2.9.dist-info/top_level.txt,sha256=0EOjbyc3hQyzjhn6iyMgsEseqA66Xz0p27iBN7G7W1w,4
78
+ pam_python-0.2.9.dist-info/RECORD,,