pdfdancer-client-python 0.3.13__tar.gz → 3.0.1__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 (103) hide show
  1. pdfdancer_client_python-3.0.1/.autosnap.toml +10 -0
  2. pdfdancer_client_python-3.0.1/.github/dependabot.yml +6 -0
  3. {pdfdancer_client_python-0.3.13 → pdfdancer_client_python-3.0.1}/.github/workflows/ci.yml +48 -28
  4. {pdfdancer_client_python-0.3.13 → pdfdancer_client_python-3.0.1}/.github/workflows/daily-tests.yml +36 -37
  5. pdfdancer_client_python-3.0.1/.github/workflows/release.yml +56 -0
  6. {pdfdancer_client_python-0.3.13 → pdfdancer_client_python-3.0.1}/.github/workflows/sdk-backward-compat.yml +12 -8
  7. {pdfdancer_client_python-0.3.13 → pdfdancer_client_python-3.0.1}/.gitignore +3 -0
  8. pdfdancer_client_python-3.0.1/MANIFEST.in +1 -0
  9. pdfdancer_client_python-3.0.1/Makefile +80 -0
  10. pdfdancer_client_python-3.0.1/PKG-INFO +521 -0
  11. {pdfdancer_client_python-0.3.13 → pdfdancer_client_python-3.0.1}/README.md +181 -142
  12. {pdfdancer_client_python-0.3.13 → pdfdancer_client_python-3.0.1}/docs/capabilities/CLEAR_CLIPPING.md +2 -4
  13. {pdfdancer_client_python-0.3.13 → pdfdancer_client_python-3.0.1}/pyproject.toml +8 -8
  14. {pdfdancer_client_python-0.3.13 → pdfdancer_client_python-3.0.1}/src/pdfdancer/__init__.py +79 -22
  15. pdfdancer_client_python-3.0.1/src/pdfdancer/_runtime_version.py +30 -0
  16. pdfdancer_client_python-3.0.1/src/pdfdancer/_version.py +24 -0
  17. {pdfdancer_client_python-0.3.13 → pdfdancer_client_python-3.0.1}/src/pdfdancer/image_builder.py +23 -3
  18. {pdfdancer_client_python-0.3.13 → pdfdancer_client_python-3.0.1}/src/pdfdancer/models.py +127 -470
  19. {pdfdancer_client_python-0.3.13 → pdfdancer_client_python-3.0.1}/src/pdfdancer/page_builder.py +6 -17
  20. {pdfdancer_client_python-0.3.13 → pdfdancer_client_python-3.0.1}/src/pdfdancer/path_builder.py +127 -6
  21. pdfdancer_client_python-0.3.13/src/pdfdancer/pdfdancer_v1.py → pdfdancer_client_python-3.0.1/src/pdfdancer/pdfdancer_v2.py +850 -1316
  22. pdfdancer_client_python-3.0.1/src/pdfdancer/text_editing.py +1472 -0
  23. pdfdancer_client_python-3.0.1/src/pdfdancer/types.py +536 -0
  24. pdfdancer_client_python-3.0.1/src/pdfdancer_client_python.egg-info/PKG-INFO +521 -0
  25. pdfdancer_client_python-3.0.1/src/pdfdancer_client_python.egg-info/SOURCES.txt +41 -0
  26. {pdfdancer_client_python-0.3.13 → pdfdancer_client_python-3.0.1}/src/pdfdancer_client_python.egg-info/requires.txt +1 -1
  27. pdfdancer_client_python-0.3.13/.claude/commands/discuss.md +0 -4
  28. pdfdancer_client_python-0.3.13/.claude/commands/implement-new-api-features.md +0 -8
  29. pdfdancer_client_python-0.3.13/.github/workflows/release.yml +0 -84
  30. pdfdancer_client_python-0.3.13/CLAUDE.md +0 -206
  31. pdfdancer_client_python-0.3.13/PKG-INFO +0 -685
  32. pdfdancer_client_python-0.3.13/release.py +0 -318
  33. pdfdancer_client_python-0.3.13/src/pdfdancer/paragraph_builder.py +0 -554
  34. pdfdancer_client_python-0.3.13/src/pdfdancer/text_line_builder.py +0 -290
  35. pdfdancer_client_python-0.3.13/src/pdfdancer/types.py +0 -841
  36. pdfdancer_client_python-0.3.13/src/pdfdancer_client_python.egg-info/PKG-INFO +0 -685
  37. pdfdancer_client_python-0.3.13/src/pdfdancer_client_python.egg-info/SOURCES.txt +0 -89
  38. pdfdancer_client_python-0.3.13/tests/__init__.py +0 -1
  39. pdfdancer_client_python-0.3.13/tests/conftest.py +0 -31
  40. pdfdancer_client_python-0.3.13/tests/e2e/__init__.py +0 -61
  41. pdfdancer_client_python-0.3.13/tests/e2e/pdf_assertions.py +0 -1783
  42. pdfdancer_client_python-0.3.13/tests/e2e/test_acroform.py +0 -100
  43. pdfdancer_client_python-0.3.13/tests/e2e/test_bezier_builder.py +0 -283
  44. pdfdancer_client_python-0.3.13/tests/e2e/test_clipping.py +0 -323
  45. pdfdancer_client_python-0.3.13/tests/e2e/test_context_manager.py +0 -437
  46. pdfdancer_client_python-0.3.13/tests/e2e/test_form_x_objects.py +0 -38
  47. pdfdancer_client_python-0.3.13/tests/e2e/test_image.py +0 -133
  48. pdfdancer_client_python-0.3.13/tests/e2e/test_image_transform.py +0 -684
  49. pdfdancer_client_python-0.3.13/tests/e2e/test_line.py +0 -140
  50. pdfdancer_client_python-0.3.13/tests/e2e/test_line_builder.py +0 -255
  51. pdfdancer_client_python-0.3.13/tests/e2e/test_new_pdf.py +0 -143
  52. pdfdancer_client_python-0.3.13/tests/e2e/test_page.py +0 -167
  53. pdfdancer_client_python-0.3.13/tests/e2e/test_paragraph.py +0 -717
  54. pdfdancer_client_python-0.3.13/tests/e2e/test_path.py +0 -128
  55. pdfdancer_client_python-0.3.13/tests/e2e/test_path_builder.py +0 -403
  56. pdfdancer_client_python-0.3.13/tests/e2e/test_path_builder_rectangle.py +0 -515
  57. pdfdancer_client_python-0.3.13/tests/e2e/test_path_comprehensive.py +0 -552
  58. pdfdancer_client_python-0.3.13/tests/e2e/test_path_group.py +0 -244
  59. pdfdancer_client_python-0.3.13/tests/e2e/test_pdfdancer.py +0 -55
  60. pdfdancer_client_python-0.3.13/tests/e2e/test_positioning.py +0 -2
  61. pdfdancer_client_python-0.3.13/tests/e2e/test_rectangle_builder.py +0 -449
  62. pdfdancer_client_python-0.3.13/tests/e2e/test_redact.py +0 -290
  63. pdfdancer_client_python-0.3.13/tests/e2e/test_singular_selection.py +0 -328
  64. pdfdancer_client_python-0.3.13/tests/e2e/test_snapshot.py +0 -295
  65. pdfdancer_client_python-0.3.13/tests/e2e/test_template_replace.py +0 -276
  66. pdfdancer_client_python-0.3.13/tests/e2e/test_template_replace_linebreak.py +0 -141
  67. pdfdancer_client_python-0.3.13/tests/e2e/test_text_line_edit.py +0 -270
  68. pdfdancer_client_python-0.3.13/tests/fixtures/Asimovian-Regular.ttf +0 -0
  69. pdfdancer_client_python-0.3.13/tests/fixtures/DancingScript-Regular.ttf +0 -0
  70. pdfdancer_client_python-0.3.13/tests/fixtures/Empty.pdf +0 -0
  71. pdfdancer_client_python-0.3.13/tests/fixtures/JetBrainsMono-Regular.ttf +0 -0
  72. pdfdancer_client_python-0.3.13/tests/fixtures/Roboto-Regular.ttf +0 -0
  73. pdfdancer_client_python-0.3.13/tests/fixtures/Showcase.pdf +0 -0
  74. pdfdancer_client_python-0.3.13/tests/fixtures/basic-paths.pdf +0 -0
  75. pdfdancer_client_python-0.3.13/tests/fixtures/form-xobject-example.pdf +0 -0
  76. pdfdancer_client_python-0.3.13/tests/fixtures/invisible-content-clipping-test.pdf +0 -0
  77. pdfdancer_client_python-0.3.13/tests/fixtures/logo-80.png +0 -0
  78. pdfdancer_client_python-0.3.13/tests/fixtures/mixed-form-types.pdf +0 -0
  79. pdfdancer_client_python-0.3.13/tests/test_anonymous_token.py +0 -222
  80. pdfdancer_client_python-0.3.13/tests/test_fingerprint.py +0 -120
  81. pdfdancer_client_python-0.3.13/tests/test_models.py +0 -371
  82. pdfdancer_client_python-0.3.13/tests/test_openapi_compliance.py +0 -129
  83. pdfdancer_client_python-0.3.13/tests/test_path_models.py +0 -256
  84. pdfdancer_client_python-0.3.13/tests/test_pdf_object_equality.py +0 -320
  85. pdfdancer_client_python-0.3.13/tests/test_rate_limit.py +0 -82
  86. pdfdancer_client_python-0.3.13/tests/test_standard_fonts.py +0 -174
  87. {pdfdancer_client_python-0.3.13 → pdfdancer_client_python-3.0.1}/.flake8 +0 -0
  88. {pdfdancer_client_python-0.3.13 → pdfdancer_client_python-3.0.1}/.gitmodules +0 -0
  89. {pdfdancer_client_python-0.3.13 → pdfdancer_client_python-3.0.1}/LICENSE +0 -0
  90. {pdfdancer_client_python-0.3.13 → pdfdancer_client_python-3.0.1}/NOTICE +0 -0
  91. {pdfdancer_client_python-0.3.13 → pdfdancer_client_python-3.0.1}/TODO.md +0 -0
  92. {pdfdancer_client_python-0.3.13 → pdfdancer_client_python-3.0.1}/check.py +0 -0
  93. {pdfdancer_client_python-0.3.13 → pdfdancer_client_python-3.0.1}/docs/capabilities/.gitkeep +0 -0
  94. {pdfdancer_client_python-0.3.13 → pdfdancer_client_python-3.0.1}/media/logo-orange-512h.webp +0 -0
  95. {pdfdancer_client_python-0.3.13 → pdfdancer_client_python-3.0.1}/media/logo-orange-60h.webp +0 -0
  96. {pdfdancer_client_python-0.3.13 → pdfdancer_client_python-3.0.1}/media/logo-silver-512h.webp +0 -0
  97. {pdfdancer_client_python-0.3.13 → pdfdancer_client_python-3.0.1}/media/logo-silver-60h.webp +0 -0
  98. {pdfdancer_client_python-0.3.13 → pdfdancer_client_python-3.0.1}/setup.cfg +0 -0
  99. {pdfdancer_client_python-0.3.13 → pdfdancer_client_python-3.0.1}/src/pdfdancer/exceptions.py +0 -0
  100. {pdfdancer_client_python-0.3.13 → pdfdancer_client_python-3.0.1}/src/pdfdancer/fingerprint.py +0 -0
  101. {pdfdancer_client_python-0.3.13 → pdfdancer_client_python-3.0.1}/src/pdfdancer_client_python.egg-info/dependency_links.txt +0 -0
  102. {pdfdancer_client_python-0.3.13 → pdfdancer_client_python-3.0.1}/src/pdfdancer_client_python.egg-info/top_level.txt +0 -0
  103. {pdfdancer_client_python-0.3.13 → pdfdancer_client_python-3.0.1}/test.sh +0 -0
@@ -0,0 +1,10 @@
1
+ check = "make check"
2
+ idle_seconds = 60
3
+ snapshot_mode = "both"
4
+ commit_mode = "checkpoint"
5
+ msg_source_cmd = 'gommit -A -n --diff-base "$AUTOSNAP_DIFF_BASE"'
6
+ log_max_bytes = 1048576
7
+
8
+ [watch]
9
+ mode = "recursive"
10
+ poll_interval = "5s"
@@ -0,0 +1,6 @@
1
+ version: 2
2
+ updates:
3
+ - package-ecosystem: github-actions
4
+ directory: /
5
+ schedule:
6
+ interval: monthly
@@ -9,8 +9,8 @@ on:
9
9
 
10
10
  jobs:
11
11
  test:
12
- # Quick minimal test on non-main branches
13
- if: github.ref != 'refs/heads/main'
12
+ # Run the minimal matrix for automatic non-main events.
13
+ if: github.event_name != 'workflow_dispatch' && github.ref != 'refs/heads/main'
14
14
  runs-on: ubuntu-latest
15
15
  permissions:
16
16
  contents: read
@@ -23,15 +23,15 @@ jobs:
23
23
  python-version: [ '3.12' ]
24
24
 
25
25
  steps:
26
- - uses: actions/checkout@v4
26
+ - uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4
27
27
  with:
28
28
  submodules: true
29
29
 
30
30
  - name: Resolve API base URL
31
31
  id: resolve-api-base-url
32
- uses: actions/github-script@v7
32
+ uses: actions/github-script@f28e40c7f34bde8b3046d885e986cb6290c5673b # v7
33
33
  with:
34
- github-token: ${{ secrets.PROPAGATE_TOKEN }}
34
+ github-token: ${{ secrets.PROPAGATE_TOKEN || github.token }}
35
35
  script: |
36
36
  const fallbackUrl = 'https://api-staging.pdfdancer.com';
37
37
  const pr = context.payload.pull_request;
@@ -41,7 +41,7 @@ jobs:
41
41
  }
42
42
 
43
43
  const body = pr.body || '';
44
- const apiPrMatch = body.match(/## Source API PR[\s\S]*?^- Repository:\s*MenschMachine\/pdfdancer-api\s*$[\s\S]*?^- PR:\s*#?(\d+)\s*$/im);
44
+ const apiPrMatch = body.match(/## Source API PR[\s\S]*?^\s+- Repository:\s*MenschMachine\/pdfdancer-api\s*$[\s\S]*?^\s+- PR:\s*#?(\d+)\s*$/im);
45
45
  if (!apiPrMatch) {
46
46
  core.info('No Source API PR block found in the PR body; using staging API.');
47
47
  core.setOutput('base_url', fallbackUrl);
@@ -74,7 +74,7 @@ jobs:
74
74
  core.setOutput('base_url', urlMatch[1]);
75
75
 
76
76
  - name: Set up Python ${{ matrix.python-version }}
77
- uses: actions/setup-python@v5
77
+ uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5
78
78
  with:
79
79
  python-version: ${{ matrix.python-version }}
80
80
 
@@ -92,9 +92,11 @@ jobs:
92
92
 
93
93
  - name: Run tests
94
94
  run: |
95
- PDFDANCER_BASE_URL=${{ steps.resolve-api-base-url.outputs.base_url }} \
96
- PDFDANCER_API_TOKEN=42 \
97
- venv/bin/python -m pytest tests/ -v --maxfail=3
95
+ mkdir -p test-results
96
+ venv/bin/python -m pytest tests/ -v --maxfail=3 --junitxml=test-results/junit.xml
97
+ env:
98
+ PDFDANCER_BASE_URL: ${{ steps.resolve-api-base-url.outputs.base_url }}
99
+ PDFDANCER_API_TOKEN: 42
98
100
 
99
101
  - name: Build distribution packages
100
102
  run: venv/bin/python -m build
@@ -102,10 +104,21 @@ jobs:
102
104
  - name: Validate packages
103
105
  run: venv/bin/python -m twine check dist/*
104
106
 
107
+ - name: Upload test results
108
+ if: failure()
109
+ uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4
110
+ with:
111
+ name: test-results-python-${{ matrix.python-version }}
112
+ path: |
113
+ test-results/
114
+ .pytest_cache/
115
+ if-no-files-found: ignore
116
+ retention-days: 7
117
+
105
118
 
106
119
  test-full-matrix:
107
- # Full matrix only on main
108
- if: github.ref == 'refs/heads/main'
120
+ # Run the full matrix on main and for every manual dispatch.
121
+ if: github.event_name == 'workflow_dispatch' || github.ref == 'refs/heads/main'
109
122
  runs-on: ${{ matrix.os }}
110
123
  permissions:
111
124
  contents: read
@@ -119,20 +132,12 @@ jobs:
119
132
  python-version: [ '3.10', '3.11', '3.12', '3.13' ]
120
133
 
121
134
  steps:
122
- - uses: actions/checkout@v4
135
+ - uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4
123
136
  with:
124
137
  submodules: true
125
138
 
126
- - name: Resolve API base URL
127
- id: resolve-api-base-url
128
- uses: actions/github-script@v7
129
- with:
130
- github-token: ${{ secrets.PROPAGATE_TOKEN }}
131
- script: |
132
- core.setOutput('base_url', 'https://api-staging.pdfdancer.com');
133
-
134
139
  - name: Set up Python ${{ matrix.python-version }}
135
- uses: actions/setup-python@v5
140
+ uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5
136
141
  with:
137
142
  python-version: ${{ matrix.python-version }}
138
143
 
@@ -154,9 +159,11 @@ jobs:
154
159
  - name: Run tests (Unix)
155
160
  if: runner.os != 'Windows'
156
161
  run: |
157
- PDFDANCER_BASE_URL=${{ steps.resolve-api-base-url.outputs.base_url }} \
158
- PDFDANCER_API_TOKEN=42 \
159
- venv/bin/python -m pytest tests/ -v --maxfail=3
162
+ mkdir -p test-results
163
+ venv/bin/python -m pytest tests/ -v --maxfail=3 --junitxml=test-results/junit.xml
164
+ env:
165
+ PDFDANCER_BASE_URL: https://api-staging.pdfdancer.com
166
+ PDFDANCER_API_TOKEN: 42
160
167
 
161
168
  - name: Build & Validate (Unix)
162
169
  if: runner.os != 'Windows'
@@ -185,9 +192,11 @@ jobs:
185
192
  if: runner.os == 'Windows'
186
193
  shell: cmd
187
194
  run: |
188
- set PDFDANCER_BASE_URL=${{ steps.resolve-api-base-url.outputs.base_url }}
189
- set PDFDANCER_API_TOKEN=42
190
- venv\Scripts\python -m pytest tests/ -v --maxfail=3
195
+ if not exist test-results mkdir test-results
196
+ venv\Scripts\python -m pytest tests/ -v --maxfail=3 --junitxml=test-results/junit.xml
197
+ env:
198
+ PDFDANCER_BASE_URL: https://api-staging.pdfdancer.com
199
+ PDFDANCER_API_TOKEN: 42
191
200
 
192
201
  - name: Build & Validate (Windows)
193
202
  if: runner.os == 'Windows'
@@ -195,3 +204,14 @@ jobs:
195
204
  run: |
196
205
  venv\Scripts\python -m build
197
206
  venv\Scripts\python -m twine check dist/*
207
+
208
+ - name: Upload test results
209
+ if: failure()
210
+ uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4
211
+ with:
212
+ name: test-results-${{ matrix.os }}-python-${{ matrix.python-version }}
213
+ path: |
214
+ test-results/
215
+ .pytest_cache/
216
+ if-no-files-found: ignore
217
+ retention-days: 7
@@ -2,12 +2,14 @@ name: Daily Tests
2
2
 
3
3
  on:
4
4
  schedule:
5
- # Run daily at 9:00 PM UTC
5
+ # Run daily at 10:40 UTC
6
6
  - cron: '40 10 * * *'
7
7
  workflow_dispatch: # Allow manual triggering
8
8
 
9
9
  jobs:
10
10
  daily-test:
11
+ permissions:
12
+ contents: read
11
13
  runs-on: ${{ matrix.os }}
12
14
  strategy:
13
15
  fail-fast: false
@@ -17,12 +19,12 @@ jobs:
17
19
  python-version: [ '3.10', '3.11', '3.12', '3.13' ]
18
20
 
19
21
  steps:
20
- - uses: actions/checkout@v4
22
+ - uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4
21
23
  with:
22
24
  submodules: true
23
25
 
24
26
  - name: Set up Python ${{ matrix.python-version }}
25
- uses: actions/setup-python@v5
27
+ uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5
26
28
  with:
27
29
  python-version: ${{ matrix.python-version }}
28
30
 
@@ -44,9 +46,11 @@ jobs:
44
46
  - name: Run tests (Unix)
45
47
  if: runner.os != 'Windows'
46
48
  run: |
47
- PDFDANCER_BASE_URL=https://api-staging.pdfdancer.com \
48
- PDFDANCER_TOKEN=42 \
49
- venv/bin/python -m pytest tests/ -v --maxfail=3
49
+ mkdir -p test-results
50
+ venv/bin/python -m pytest tests/ -v --maxfail=3 --junitxml=test-results/junit.xml
51
+ env:
52
+ PDFDANCER_BASE_URL: https://api-staging.pdfdancer.com
53
+ PDFDANCER_API_TOKEN: 42
50
54
 
51
55
  - name: Build & Validate (Unix)
52
56
  if: runner.os != 'Windows'
@@ -75,9 +79,11 @@ jobs:
75
79
  if: runner.os == 'Windows'
76
80
  shell: cmd
77
81
  run: |
78
- set PDFDANCER_BASE_URL=https://api-staging.pdfdancer.com
79
- set PDFDANCER_TOKEN=42
80
- venv\Scripts\python -m pytest tests/ -v --maxfail=3
82
+ if not exist test-results mkdir test-results
83
+ venv\Scripts\python -m pytest tests/ -v --maxfail=3 --junitxml=test-results/junit.xml
84
+ env:
85
+ PDFDANCER_BASE_URL: https://api-staging.pdfdancer.com
86
+ PDFDANCER_API_TOKEN: 42
81
87
 
82
88
  - name: Build & Validate (Windows)
83
89
  if: runner.os == 'Windows'
@@ -86,34 +92,27 @@ jobs:
86
92
  venv\Scripts\python -m build
87
93
  venv\Scripts\python -m twine check dist/*
88
94
 
89
- notify-on-failure:
95
+ - name: Upload test results
96
+ if: failure()
97
+ uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4
98
+ with:
99
+ name: test-results-${{ matrix.os }}-python-${{ matrix.python-version }}
100
+ path: |
101
+ test-results/
102
+ .pytest_cache/
103
+ if-no-files-found: ignore
104
+ retention-days: 7
105
+
106
+ notify:
90
107
  needs: daily-test
91
108
  runs-on: ubuntu-latest
92
- if: failure()
109
+ if: always() && needs.daily-test.result == 'failure'
110
+ permissions:
111
+ contents: read
93
112
  steps:
94
- - name: Create issue on failure
95
- uses: actions/github-script@v7
96
- with:
97
- script: |
98
- const title = `Daily Tests Failed - ${new Date().toISOString().split('T')[0]}`;
99
- const body = `The daily test run has failed. Please check the workflow run for details.\n\n[Workflow Run](${context.payload.repository.html_url}/actions/runs/${context.runId})`;
100
-
101
- // Check if an issue already exists
102
- const issues = await github.rest.issues.listForRepo({
103
- owner: context.repo.owner,
104
- repo: context.repo.repo,
105
- state: 'open',
106
- labels: 'daily-test-failure'
107
- });
108
-
109
- const existingIssue = issues.data.find(issue => issue.title === title);
110
-
111
- if (!existingIssue) {
112
- await github.rest.issues.create({
113
- owner: context.repo.owner,
114
- repo: context.repo.repo,
115
- title: title,
116
- body: body,
117
- labels: ['daily-test-failure', 'automated']
118
- });
119
- }
113
+ - name: Summarize failure
114
+ run: |
115
+ echo "## Daily Tests Failed" >> "$GITHUB_STEP_SUMMARY"
116
+ echo "" >> "$GITHUB_STEP_SUMMARY"
117
+ echo "Python SDK daily tests failed on $(date -u +%F)." >> "$GITHUB_STEP_SUMMARY"
118
+ echo "[Open workflow run](${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }})" >> "$GITHUB_STEP_SUMMARY"
@@ -0,0 +1,56 @@
1
+ name: Release
2
+
3
+ on:
4
+ push:
5
+ tags:
6
+ - 'v*'
7
+
8
+ permissions:
9
+ contents: write
10
+ id-token: write
11
+
12
+ jobs:
13
+ release:
14
+ runs-on: ubuntu-latest
15
+ steps:
16
+ - uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4
17
+ with:
18
+ fetch-depth: 0
19
+ submodules: true
20
+
21
+ - name: Set up Python 3.12
22
+ uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5
23
+ with:
24
+ python-version: '3.12'
25
+
26
+ - name: Pin package version from tag
27
+ run: echo "SETUPTOOLS_SCM_PRETEND_VERSION=${GITHUB_REF_NAME#v}" >> "$GITHUB_ENV"
28
+
29
+ - name: Install dependencies
30
+ run: |
31
+ python -m pip install --upgrade pip
32
+ python -m pip install -e ".[dev]"
33
+
34
+ - name: Lint
35
+ run: python -m flake8 src/
36
+
37
+ - name: Tests
38
+ run: python -m pytest tests/ -v --maxfail=1 --ignore=tests/e2e/test_pdfdancer.py
39
+ env:
40
+ PDFDANCER_BASE_URL: https://api.pdfdancer.com
41
+ PDFDANCER_API_TOKEN: ${{ secrets.PDFDANCER_API_TOKEN_PRODUCTION }}
42
+
43
+ - name: Build package
44
+ run: python -m build
45
+
46
+ - name: Validate packages
47
+ run: python -m twine check dist/*
48
+
49
+ - name: Publish to PyPI
50
+ uses: pypa/gh-action-pypi-publish@cef221092ed1bacb1cc03d23a2d87d1d172e277b # release/v1
51
+
52
+ - name: Create GitHub Release
53
+ uses: softprops/action-gh-release@3bb12739c298aeb8a4eeaf626c5b8d85266b0e65 # v2
54
+ with:
55
+ generate_release_notes: true
56
+ files: dist/*
@@ -11,11 +11,14 @@ on:
11
11
  required: true
12
12
  type: string
13
13
  sdk_token:
14
- description: 'API token for testing'
14
+ description: 'Non-secret test token; never enter production credentials'
15
15
  required: true
16
16
  type: string
17
17
  default: '42'
18
18
 
19
+ permissions:
20
+ contents: read
21
+
19
22
  jobs:
20
23
  get-tags:
21
24
  runs-on: ubuntu-latest
@@ -47,14 +50,15 @@ jobs:
47
50
 
48
51
  steps:
49
52
  - name: Checkout SDK at ${{ matrix.tag }}
50
- uses: actions/checkout@v4
53
+ uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4
51
54
  with:
52
55
  repository: MenschMachine/pdfdancer-client-python
53
56
  ref: ${{ matrix.tag }}
54
57
  path: sdk
58
+ submodules: true
55
59
 
56
60
  - name: Set up Python
57
- uses: actions/setup-python@v5
61
+ uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5
58
62
  with:
59
63
  python-version: '3.12'
60
64
 
@@ -69,20 +73,20 @@ jobs:
69
73
  - name: Run e2e tests
70
74
  working-directory: sdk
71
75
  run: |
72
- PDFDANCER_BASE_URL=${{ github.event.client_payload.api_url || inputs.api_url }} \
73
- PDFDANCER_API_TOKEN=${{ github.event.client_payload.sdk_token || inputs.sdk_token }} \
74
76
  ../venv/bin/python -m pytest -v -x \
75
77
  -k "not test_redact_multiple_paths and not test_context_manager_vs_manual_apply" \
76
78
  --junitxml=test-results.xml
77
79
  env:
78
- PYTHONWARNINGS: "ignore:Unverified HTTPS request"
80
+ PDFDANCER_BASE_URL: ${{ github.event.client_payload.api_url || inputs.api_url }}
81
+ PDFDANCER_API_TOKEN: ${{ github.event.client_payload.sdk_token || inputs.sdk_token }}
79
82
 
80
83
  - name: Upload test results
81
84
  if: failure()
82
- uses: actions/upload-artifact@v4
85
+ uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4
83
86
  with:
84
87
  name: test-results-${{ matrix.tag }}
85
88
  path: |
86
89
  sdk/test-results.xml
87
90
  sdk/.pytest_cache/
88
- retention-days: 7
91
+ if-no-files-found: ignore
92
+ retention-days: 7
@@ -21,3 +21,6 @@ __pycache__/
21
21
  .env
22
22
  .propagate*md
23
23
  /.last-message.txt
24
+
25
+ # Auto-generated by setuptools_scm
26
+ src/pdfdancer/_version.py
@@ -0,0 +1 @@
1
+ prune tests
@@ -0,0 +1,80 @@
1
+ BASE_PYTHON ?= python
2
+ VENV ?= venv
3
+ TEST_PATH ?= tests
4
+ PYTEST_ARGS ?= -v
5
+
6
+ ifeq ($(OS),Windows_NT)
7
+ VENV_PYTHON := $(VENV)/Scripts/python.exe
8
+ else
9
+ VENV_PYTHON := $(VENV)/bin/python
10
+ endif
11
+
12
+ PYTHON ?= $(VENV_PYTHON)
13
+
14
+ CODE_DIRS := src tests
15
+ FIXTURE_DIR := tests/fixtures
16
+ UNIT_TEST_ARGS := --ignore=tests/e2e
17
+
18
+ .DEFAULT_GOAL := help
19
+
20
+ .PHONY: help venv install install-dev format format-check lint typecheck quality \
21
+ test test-unit test-e2e coverage check build check-dist package clean
22
+
23
+ help: ## Show available targets and configurable variables
24
+ @awk 'BEGIN {FS = ":.*## "; printf "Usage: make <target> [BASE_PYTHON=python] [VENV=venv] [PYTHON=<venv-python>] [TEST_PATH=tests] [PYTEST_ARGS=\"-v\"]\n\nTargets:\n"} /^[a-zA-Z0-9_-]+:.*## / {printf " %-14s %s\n", $$1, $$2}' $(MAKEFILE_LIST)
25
+
26
+ venv: $(VENV_PYTHON) ## Create the project virtual environment
27
+
28
+ $(VENV_PYTHON):
29
+ $(BASE_PYTHON) -m venv $(VENV)
30
+ $(VENV_PYTHON) -m pip install --upgrade pip
31
+
32
+ install: venv ## Install the package in editable mode
33
+ $(PYTHON) -m pip install -e .
34
+
35
+ install-dev: venv ## Install the package and development dependencies in editable mode
36
+ $(PYTHON) -m pip install -e ".[dev]"
37
+
38
+ format: ## Format source code and tests with Black and isort
39
+ $(PYTHON) -m black --extend-exclude '(^|/)tests/fixtures/' $(CODE_DIRS)
40
+ $(PYTHON) -m isort --skip $(FIXTURE_DIR) $(CODE_DIRS)
41
+
42
+ format-check: ## Check formatting without changing files
43
+ $(PYTHON) -m black --check --extend-exclude '(^|/)tests/fixtures/' $(CODE_DIRS)
44
+ $(PYTHON) -m isort --check-only --skip $(FIXTURE_DIR) $(CODE_DIRS)
45
+
46
+ lint: ## Lint source code and tests with Flake8
47
+ $(PYTHON) -m flake8 --exclude=$(FIXTURE_DIR) $(CODE_DIRS)
48
+
49
+ typecheck: ## Type-check the package with mypy
50
+ $(PYTHON) -m mypy src
51
+
52
+ quality: format-check lint typecheck ## Run all non-mutating code-quality checks
53
+
54
+ test: ## Run all tests; override TEST_PATH or PYTEST_ARGS as needed
55
+ $(PYTHON) -m pytest $(TEST_PATH) $(PYTEST_ARGS)
56
+
57
+ test-unit: ## Run tests that do not require the PDFDancer API
58
+ $(PYTHON) -m pytest tests $(UNIT_TEST_ARGS) $(PYTEST_ARGS)
59
+
60
+ test-e2e: ## Run API-dependent end-to-end tests
61
+ $(PYTHON) -m pytest tests/e2e $(PYTEST_ARGS)
62
+
63
+ coverage: ## Run all tests and report package coverage
64
+ $(PYTHON) -m pytest $(TEST_PATH) $(PYTEST_ARGS) --cov=pdfdancer --cov-report=term-missing
65
+
66
+ check: quality test-unit ## Run the local pre-commit checks
67
+
68
+ build: ## Build the wheel and source distribution
69
+ $(PYTHON) -m build
70
+
71
+ check-dist: ## Validate existing distribution artifacts with Twine
72
+ $(PYTHON) -m twine check dist/*
73
+
74
+ package: clean ## Clean, build, and validate distribution artifacts
75
+ $(MAKE) build
76
+ $(MAKE) check-dist
77
+
78
+ clean: ## Remove generated build, test, coverage, and Python cache artifacts
79
+ $(RM) -r build dist .pytest_cache .mypy_cache .coverage htmlcov src/*.egg-info
80
+ find src tests -type d -name __pycache__ -prune -exec $(RM) -r {} +