ForageFacebook 2.1.0__tar.gz → 2.2.0__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.
- {foragefacebook-2.1.0 → foragefacebook-2.2.0}/.github/workflows/ci.yml +4 -25
- {foragefacebook-2.1.0 → foragefacebook-2.2.0}/.gitignore +5 -0
- {foragefacebook-2.1.0 → foragefacebook-2.2.0}/AGENTS.md +9 -7
- {foragefacebook-2.1.0 → foragefacebook-2.2.0}/CHANGELOG.md +12 -1
- {foragefacebook-2.1.0 → foragefacebook-2.2.0}/CONTRIBUTING.md +6 -4
- foragefacebook-2.2.0/Makefile +14 -0
- {foragefacebook-2.1.0 → foragefacebook-2.2.0}/PKG-INFO +15 -6
- {foragefacebook-2.1.0 → foragefacebook-2.2.0}/README.md +14 -4
- {foragefacebook-2.1.0 → foragefacebook-2.2.0}/docs/capabilities.md +1 -1
- {foragefacebook-2.1.0 → foragefacebook-2.2.0}/pyproject.toml +1 -2
- foragefacebook-2.2.0/scripts/live-e2e.py +57 -0
- {foragefacebook-2.1.0 → foragefacebook-2.2.0}/src/forage/__init__.py +1 -1
- {foragefacebook-2.1.0 → foragefacebook-2.2.0}/src/forage/cli.py +11 -2
- {foragefacebook-2.1.0 → foragefacebook-2.2.0}/uv.lock +2 -110
- foragefacebook-2.1.0/tests/__init__.py +0 -1
- foragefacebook-2.1.0/tests/conftest.py +0 -43
- foragefacebook-2.1.0/tests/fixtures/comment_simple.html +0 -7
- foragefacebook-2.1.0/tests/fixtures/comment_with_replies.html +0 -23
- foragefacebook-2.1.0/tests/fixtures/post_long_content.html +0 -13
- foragefacebook-2.1.0/tests/fixtures/post_realistic.html +0 -54
- foragefacebook-2.1.0/tests/fixtures/post_simple.html +0 -13
- foragefacebook-2.1.0/tests/fixtures/post_sponsored.html +0 -7
- foragefacebook-2.1.0/tests/fixtures/post_with_emoji.html +0 -12
- foragefacebook-2.1.0/tests/fixtures/post_with_media.html +0 -15
- foragefacebook-2.1.0/tests/fixtures/post_with_see_more.html +0 -12
- foragefacebook-2.1.0/tests/test_auth.py +0 -48
- foragefacebook-2.1.0/tests/test_cli.py +0 -161
- foragefacebook-2.1.0/tests/test_exporter.py +0 -669
- foragefacebook-2.1.0/tests/test_marketplace.py +0 -56
- foragefacebook-2.1.0/tests/test_models.py +0 -158
- foragefacebook-2.1.0/tests/test_parser.py +0 -362
- foragefacebook-2.1.0/tests/test_regressions.py +0 -352
- foragefacebook-2.1.0/tests/test_scraper.py +0 -473
- {foragefacebook-2.1.0 → foragefacebook-2.2.0}/.github/FUNDING.yml +0 -0
- {foragefacebook-2.1.0 → foragefacebook-2.2.0}/.github/dependabot.yml +0 -0
- {foragefacebook-2.1.0 → foragefacebook-2.2.0}/.github/workflows/publish.yml +0 -0
- {foragefacebook-2.1.0 → foragefacebook-2.2.0}/CLAUDE.md +0 -0
- {foragefacebook-2.1.0 → foragefacebook-2.2.0}/LICENSE +0 -0
- {foragefacebook-2.1.0 → foragefacebook-2.2.0}/SECURITY.md +0 -0
- {foragefacebook-2.1.0 → foragefacebook-2.2.0}/docs/research/2026-09-20-cli-portfolio-audit.md +0 -0
- {foragefacebook-2.1.0 → foragefacebook-2.2.0}/docs/research/2026-09-20-cli-portfolio-repairs.md +0 -0
- {foragefacebook-2.1.0 → foragefacebook-2.2.0}/scripts/release.sh +0 -0
- {foragefacebook-2.1.0 → foragefacebook-2.2.0}/src/forage/auth.py +0 -0
- {foragefacebook-2.1.0 → foragefacebook-2.2.0}/src/forage/exporter.py +0 -0
- {foragefacebook-2.1.0 → foragefacebook-2.2.0}/src/forage/models.py +0 -0
- {foragefacebook-2.1.0 → foragefacebook-2.2.0}/src/forage/parser.py +0 -0
- {foragefacebook-2.1.0 → foragefacebook-2.2.0}/src/forage/scraper.py +0 -0
|
@@ -16,26 +16,6 @@ concurrency:
|
|
|
16
16
|
cancel-in-progress: ${{ github.ref_type != 'tag' }}
|
|
17
17
|
|
|
18
18
|
jobs:
|
|
19
|
-
test:
|
|
20
|
-
name: test (${{ matrix.python-version }})
|
|
21
|
-
runs-on: ubuntu-latest
|
|
22
|
-
timeout-minutes: 15
|
|
23
|
-
strategy:
|
|
24
|
-
fail-fast: false
|
|
25
|
-
matrix:
|
|
26
|
-
python-version: ["3.10", "3.14"]
|
|
27
|
-
steps:
|
|
28
|
-
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
|
29
|
-
with:
|
|
30
|
-
persist-credentials: false
|
|
31
|
-
- uses: astral-sh/setup-uv@bec219d24cd3e171d82865faccec33120bb574f4 # v10.1.0
|
|
32
|
-
with:
|
|
33
|
-
version: "0.12.17"
|
|
34
|
-
python-version: ${{ matrix.python-version }}
|
|
35
|
-
- run: uv sync --locked --all-extras --no-config
|
|
36
|
-
- run: uv run --locked --no-config playwright install --with-deps chromium
|
|
37
|
-
- run: uv run --locked --no-config pytest tests/ -q
|
|
38
|
-
|
|
39
19
|
quality:
|
|
40
20
|
runs-on: ubuntu-latest
|
|
41
21
|
timeout-minutes: 15
|
|
@@ -51,8 +31,8 @@ jobs:
|
|
|
51
31
|
- name: Check source and lockfile
|
|
52
32
|
run: |
|
|
53
33
|
uv lock --check --no-config
|
|
54
|
-
uv run --locked --no-config ruff check src/
|
|
55
|
-
uv run --locked --no-config ruff format --check src/
|
|
34
|
+
uv run --locked --no-config ruff check src/ scripts/
|
|
35
|
+
uv run --locked --no-config ruff format --check src/ scripts/
|
|
56
36
|
uv run --locked --no-config ty check src/forage/
|
|
57
37
|
- name: Audit workflows and release helper
|
|
58
38
|
run: |
|
|
@@ -70,12 +50,11 @@ jobs:
|
|
|
70
50
|
|
|
71
51
|
check:
|
|
72
52
|
if: always()
|
|
73
|
-
needs: [
|
|
53
|
+
needs: [quality]
|
|
74
54
|
runs-on: ubuntu-latest
|
|
75
55
|
timeout-minutes: 2
|
|
76
56
|
steps:
|
|
77
57
|
- name: Require all checks
|
|
78
58
|
env:
|
|
79
|
-
TEST_RESULT: ${{ needs.test.result }}
|
|
80
59
|
QUALITY_RESULT: ${{ needs.quality.result }}
|
|
81
|
-
run: test "$
|
|
60
|
+
run: test "$QUALITY_RESULT" = success
|
|
@@ -12,11 +12,10 @@ Forage is a CLI tool for scraping private Facebook groups to analyze community d
|
|
|
12
12
|
uv sync # Install deps (always use uv, not pip/python)
|
|
13
13
|
uv run forage --help # Run CLI
|
|
14
14
|
uv run ty check src/ # Type check (Astral's ty)
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
uv run
|
|
18
|
-
uv run ruff
|
|
19
|
-
uv run ruff format src/ tests/ # Auto-format (CI enforces this)
|
|
15
|
+
make check # Lint, type check, and build
|
|
16
|
+
make test # Read-only live Facebook checks
|
|
17
|
+
uv run ruff check src/ scripts/ # Lint
|
|
18
|
+
uv run ruff format src/ scripts/ # Format
|
|
20
19
|
```
|
|
21
20
|
|
|
22
21
|
## Manual Testing
|
|
@@ -59,7 +58,10 @@ Random viewport sizes, `human_delay()` with variance, real Chromium browser, ses
|
|
|
59
58
|
|
|
60
59
|
### Test Structure
|
|
61
60
|
|
|
62
|
-
|
|
61
|
+
Use `make test` for real CLI reads through the saved Facebook session. Install Chromium first.
|
|
62
|
+
Set `FORAGE_E2E_GROUP` to test a group; otherwise the test searches Marketplace for computers.
|
|
63
|
+
Keep unit tests, mock tests, and fake-server suites out of this repo. CI runs static checks and package builds.
|
|
64
|
+
Live tests must not log in, change credentials, or write to Facebook.
|
|
63
65
|
|
|
64
66
|
## Fragile Code — Check Here First When Things Break
|
|
65
67
|
|
|
@@ -99,7 +101,7 @@ Steps:
|
|
|
99
101
|
1. Update `version` in both `pyproject.toml` and `src/forage/__init__.py`
|
|
100
102
|
2. Run `uv lock --no-config` — refresh locked editable package metadata without leaking user-level uv settings
|
|
101
103
|
3. Update `CHANGELOG.md` — add version section under `## [Unreleased]`, update comparison links at bottom
|
|
102
|
-
4. Run `uv lock --check --no-config` and `uv run ruff format src/
|
|
104
|
+
4. Run `uv lock --check --no-config` and `uv run ruff format src/ scripts/` — CI enforces formatting
|
|
103
105
|
5. Commit: `git commit -m "chore: release vX.Y.Z"`
|
|
104
106
|
6. Push a feature branch and open a PR. Merge only after all required checks pass. Never push directly to `master`.
|
|
105
107
|
7. Tag: `git tag -a vX.Y.Z -m "Release vX.Y.Z"` then `git push origin vX.Y.Z`
|
|
@@ -7,6 +7,16 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
7
7
|
|
|
8
8
|
## [Unreleased]
|
|
9
9
|
|
|
10
|
+
## [2.2.0] - 2026-10-10
|
|
11
|
+
|
|
12
|
+
### Changed
|
|
13
|
+
|
|
14
|
+
- Replace unit and synthetic browser tests with read-only live CLI checks.
|
|
15
|
+
- Keep lint, type checks, and package build/install checks in CI.
|
|
16
|
+
- Remove pytest and the Node test harness.
|
|
17
|
+
- Add common `make check`, `make test`, and `make build` entry points.
|
|
18
|
+
- Add `forage version` beside the existing `--version` flag.
|
|
19
|
+
|
|
10
20
|
## [2.1.0] - 2026-09-20
|
|
11
21
|
|
|
12
22
|
### Fixed
|
|
@@ -237,7 +247,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
237
247
|
- Sensitive files excluded via `.gitignore`
|
|
238
248
|
- Security guidelines in SECURITY.md
|
|
239
249
|
|
|
240
|
-
[Unreleased]: https://github.com/jwmoss/forage/compare/v2.
|
|
250
|
+
[Unreleased]: https://github.com/jwmoss/forage/compare/v2.2.0...HEAD
|
|
251
|
+
[2.2.0]: https://github.com/jwmoss/forage/compare/v2.1.0...v2.2.0
|
|
241
252
|
[2.1.0]: https://github.com/jwmoss/forage/compare/v2.0.0...v2.1.0
|
|
242
253
|
[2.0.0]: https://github.com/jwmoss/forage/compare/v1.2.1...v2.0.0
|
|
243
254
|
[1.2.1]: https://github.com/jwmoss/forage/compare/v1.2.0...v1.2.1
|
|
@@ -22,7 +22,8 @@ Thank you for your interest in contributing to Forage!
|
|
|
22
22
|
|
|
23
23
|
4. Run tests:
|
|
24
24
|
```bash
|
|
25
|
-
|
|
25
|
+
make check
|
|
26
|
+
make test
|
|
26
27
|
```
|
|
27
28
|
|
|
28
29
|
5. Run type checker:
|
|
@@ -55,15 +56,16 @@ Example: `feat: add SQLite export option`
|
|
|
55
56
|
1. Fork the repository
|
|
56
57
|
2. Create a feature branch (`git checkout -b feat/my-feature`)
|
|
57
58
|
3. Make your changes
|
|
58
|
-
4. Ensure tests pass (`
|
|
59
|
+
4. Ensure tests pass (`make check`; run `make test` with a saved session)
|
|
59
60
|
5. Ensure type checking passes (`uv run ty check src/forage/`)
|
|
60
61
|
6. Commit with a descriptive message
|
|
61
62
|
7. Push and open a pull request
|
|
62
63
|
|
|
63
64
|
## Testing
|
|
64
65
|
|
|
65
|
-
-
|
|
66
|
-
-
|
|
66
|
+
- Verify behavior through the real CLI against Facebook.
|
|
67
|
+
- Keep live checks read-only and use the existing session.
|
|
68
|
+
- Do not add unit tests, mock tests, or fake-server suites.
|
|
67
69
|
- Mock external dependencies (Playwright, network)
|
|
68
70
|
- Aim for good coverage of edge cases
|
|
69
71
|
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
.PHONY: check test build
|
|
2
|
+
|
|
3
|
+
check:
|
|
4
|
+
uv lock --check --no-config
|
|
5
|
+
uv run --locked --no-config ruff check src/ scripts/
|
|
6
|
+
uv run --locked --no-config ruff format --check src/ scripts/
|
|
7
|
+
uv run --locked --no-config ty check src/forage/
|
|
8
|
+
uv build --no-config
|
|
9
|
+
|
|
10
|
+
test:
|
|
11
|
+
uv run --locked --no-config python scripts/live-e2e.py
|
|
12
|
+
|
|
13
|
+
build:
|
|
14
|
+
uv build --no-config
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.5
|
|
2
2
|
Name: ForageFacebook
|
|
3
|
-
Version: 2.
|
|
3
|
+
Version: 2.2.0
|
|
4
4
|
Summary: CLI tool to scrape posts, comments, and reactions from private Facebook groups
|
|
5
5
|
Project-URL: Homepage, https://github.com/jwmoss/forage
|
|
6
6
|
Project-URL: Repository, https://github.com/jwmoss/forage
|
|
@@ -28,7 +28,6 @@ Requires-Dist: playwright>=1.40
|
|
|
28
28
|
Requires-Dist: pydantic>=2.0
|
|
29
29
|
Requires-Dist: rich>=13.0
|
|
30
30
|
Provides-Extra: dev
|
|
31
|
-
Requires-Dist: pytest>=8.0; extra == 'dev'
|
|
32
31
|
Requires-Dist: ruff>=0.8; extra == 'dev'
|
|
33
32
|
Requires-Dist: ty>=0.0.1a6; extra == 'dev'
|
|
34
33
|
Description-Content-Type: text/markdown
|
|
@@ -319,13 +318,23 @@ uv sync --extra dev
|
|
|
319
318
|
# Run type checker
|
|
320
319
|
uv run ty check src/
|
|
321
320
|
|
|
322
|
-
#
|
|
321
|
+
# Run static checks and build the package
|
|
322
|
+
make check
|
|
323
|
+
|
|
324
|
+
# Install Chromium for live reads
|
|
323
325
|
uv run playwright install chromium
|
|
324
326
|
|
|
325
|
-
#
|
|
326
|
-
|
|
327
|
+
# Read Marketplace through the saved Facebook session
|
|
328
|
+
make test
|
|
329
|
+
|
|
330
|
+
# Read one post from a group you can access
|
|
331
|
+
FORAGE_E2E_GROUP=your-group-slug make test
|
|
327
332
|
```
|
|
328
333
|
|
|
334
|
+
Live tests run the real CLI and Facebook pages. They do not use fixtures or mock servers.
|
|
335
|
+
They need an existing login session. CI runs static checks and package build/install checks.
|
|
336
|
+
|
|
337
|
+
|
|
329
338
|
## Architecture
|
|
330
339
|
|
|
331
340
|
```
|
|
@@ -344,7 +353,7 @@ src/forage/
|
|
|
344
353
|
- Rate limiting may require slower scraping
|
|
345
354
|
- Individual reaction types not broken out (only total)
|
|
346
355
|
- Session lifetime depends on Facebook; use `forage login` when authentication fails
|
|
347
|
-
- English page labels
|
|
356
|
+
- English page labels define the supported parser scope
|
|
348
357
|
- Previously overwritten SQLite comments require a new scrape; upgrades cannot recover them
|
|
349
358
|
|
|
350
359
|
## Roadmap
|
|
@@ -284,13 +284,23 @@ uv sync --extra dev
|
|
|
284
284
|
# Run type checker
|
|
285
285
|
uv run ty check src/
|
|
286
286
|
|
|
287
|
-
#
|
|
287
|
+
# Run static checks and build the package
|
|
288
|
+
make check
|
|
289
|
+
|
|
290
|
+
# Install Chromium for live reads
|
|
288
291
|
uv run playwright install chromium
|
|
289
292
|
|
|
290
|
-
#
|
|
291
|
-
|
|
293
|
+
# Read Marketplace through the saved Facebook session
|
|
294
|
+
make test
|
|
295
|
+
|
|
296
|
+
# Read one post from a group you can access
|
|
297
|
+
FORAGE_E2E_GROUP=your-group-slug make test
|
|
292
298
|
```
|
|
293
299
|
|
|
300
|
+
Live tests run the real CLI and Facebook pages. They do not use fixtures or mock servers.
|
|
301
|
+
They need an existing login session. CI runs static checks and package build/install checks.
|
|
302
|
+
|
|
303
|
+
|
|
294
304
|
## Architecture
|
|
295
305
|
|
|
296
306
|
```
|
|
@@ -309,7 +319,7 @@ src/forage/
|
|
|
309
319
|
- Rate limiting may require slower scraping
|
|
310
320
|
- Individual reaction types not broken out (only total)
|
|
311
321
|
- Session lifetime depends on Facebook; use `forage login` when authentication fails
|
|
312
|
-
- English page labels
|
|
322
|
+
- English page labels define the supported parser scope
|
|
313
323
|
- Previously overwritten SQLite comments require a new scrape; upgrades cannot recover them
|
|
314
324
|
|
|
315
325
|
## Roadmap
|
|
@@ -34,4 +34,4 @@ The current audit cannot retrieve that page or current [Marketplace documentatio
|
|
|
34
34
|
Current replacement availability and exhaustive REST coverage therefore remain unknown. Do not infer private APIs from DOM fields.
|
|
35
35
|
|
|
36
36
|
All new selector checks use synthetic, network-blocked Chromium pages. No live authenticated Facebook session validates this change.
|
|
37
|
-
|
|
37
|
+
Verify selector changes through read-only live CLI requests. Never publish session state or private group content.
|
|
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "ForageFacebook"
|
|
7
|
-
version = "2.
|
|
7
|
+
version = "2.2.0"
|
|
8
8
|
description = "CLI tool to scrape posts, comments, and reactions from private Facebook groups"
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
requires-python = ">=3.10"
|
|
@@ -42,7 +42,6 @@ Changelog = "https://github.com/jwmoss/forage/blob/master/CHANGELOG.md"
|
|
|
42
42
|
|
|
43
43
|
[project.optional-dependencies]
|
|
44
44
|
dev = [
|
|
45
|
-
"pytest>=8.0",
|
|
46
45
|
"ruff>=0.8",
|
|
47
46
|
"ty>=0.0.1a6",
|
|
48
47
|
]
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""Check real Facebook reads through the installed CLI and saved session."""
|
|
3
|
+
|
|
4
|
+
import json
|
|
5
|
+
import os
|
|
6
|
+
import shlex
|
|
7
|
+
import subprocess
|
|
8
|
+
import sys
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
def main():
|
|
12
|
+
cli = shlex.split(os.environ.get("FORAGE_BIN", "forage"))
|
|
13
|
+
|
|
14
|
+
def call(*args):
|
|
15
|
+
result = subprocess.run(
|
|
16
|
+
[*cli, "--no-color", *args], capture_output=True, text=True, timeout=120
|
|
17
|
+
)
|
|
18
|
+
if result.returncode:
|
|
19
|
+
raise RuntimeError(
|
|
20
|
+
f"{args[0]} failed with exit {result.returncode}; run the command to inspect the error"
|
|
21
|
+
)
|
|
22
|
+
return json.loads(result.stdout)
|
|
23
|
+
|
|
24
|
+
doctor = call("doctor")
|
|
25
|
+
if not doctor.get("browser_installed") or not doctor.get("session_exists"):
|
|
26
|
+
raise RuntimeError(
|
|
27
|
+
"Install Chromium and save a Facebook session before live tests"
|
|
28
|
+
)
|
|
29
|
+
group = os.environ.get("FORAGE_E2E_GROUP")
|
|
30
|
+
if group:
|
|
31
|
+
result = call("scrape", group, "--limit", "1", "--skip-comments", "--no-input")
|
|
32
|
+
rows = result.get("posts")
|
|
33
|
+
else:
|
|
34
|
+
result = call(
|
|
35
|
+
"marketplace", "computer", "--limit", "1", "--max-candidates", "3"
|
|
36
|
+
)
|
|
37
|
+
rows = result.get("listings")
|
|
38
|
+
if not isinstance(rows, list) or not rows or not rows[0].get("id"):
|
|
39
|
+
raise RuntimeError(
|
|
40
|
+
"Live read returned no verified record; inspect access and page layout"
|
|
41
|
+
)
|
|
42
|
+
print("PASS real Facebook CLI read")
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
if __name__ == "__main__":
|
|
46
|
+
try:
|
|
47
|
+
main()
|
|
48
|
+
except (
|
|
49
|
+
RuntimeError,
|
|
50
|
+
ValueError,
|
|
51
|
+
KeyError,
|
|
52
|
+
TypeError,
|
|
53
|
+
OSError,
|
|
54
|
+
subprocess.TimeoutExpired,
|
|
55
|
+
) as error:
|
|
56
|
+
print(f"FAIL {error}", file=sys.stderr)
|
|
57
|
+
sys.exit(1)
|
|
@@ -61,6 +61,12 @@ def main(ctx: Context, verbose: bool, quiet: bool, no_color: bool):
|
|
|
61
61
|
console.no_color = True
|
|
62
62
|
|
|
63
63
|
|
|
64
|
+
@main.command()
|
|
65
|
+
def version() -> None:
|
|
66
|
+
"""Print version information."""
|
|
67
|
+
click.echo(f"forage, version {__version__}")
|
|
68
|
+
|
|
69
|
+
|
|
64
70
|
@main.command()
|
|
65
71
|
@click.option(
|
|
66
72
|
"--browser",
|
|
@@ -162,7 +168,10 @@ def marketplace(
|
|
|
162
168
|
|
|
163
169
|
json_output = result.model_dump_json(indent=2)
|
|
164
170
|
if output:
|
|
165
|
-
|
|
171
|
+
try:
|
|
172
|
+
output.write_text(json_output, encoding="utf-8")
|
|
173
|
+
except OSError as error:
|
|
174
|
+
raise click.ClickException(str(error)) from error
|
|
166
175
|
if not ctx.quiet:
|
|
167
176
|
console.print(f"[green]Output written to {output}[/green]")
|
|
168
177
|
else:
|
|
@@ -493,7 +502,7 @@ def export_saved(
|
|
|
493
502
|
top_comments=top_comments,
|
|
494
503
|
min_pain_score=min_pain_score,
|
|
495
504
|
)
|
|
496
|
-
except ValidationError as error:
|
|
505
|
+
except (ValidationError, UnicodeDecodeError) as error:
|
|
497
506
|
raise click.ClickException("Input is not a group scrape JSON result") from error
|
|
498
507
|
except (OSError, sqlite3.Error) as error:
|
|
499
508
|
raise click.ClickException(str(error)) from error
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
version = 1
|
|
2
|
-
revision =
|
|
2
|
+
revision = 5
|
|
3
3
|
requires-python = ">=3.10"
|
|
4
4
|
|
|
5
5
|
[[package]]
|
|
@@ -32,21 +32,9 @@ wheels = [
|
|
|
32
32
|
{ url = "https://files.pythonhosted.org/packages/d1/d6/3965ed04c63042e047cb6a3e6ed1a63a35087b6a609aa3a15ed8ac56c221/colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6", size = 25335, upload-time = "2022-10-25T02:36:20.889Z" },
|
|
33
33
|
]
|
|
34
34
|
|
|
35
|
-
[[package]]
|
|
36
|
-
name = "exceptiongroup"
|
|
37
|
-
version = "1.3.1"
|
|
38
|
-
source = { registry = "https://pypi.org/simple" }
|
|
39
|
-
dependencies = [
|
|
40
|
-
{ name = "typing-extensions" },
|
|
41
|
-
]
|
|
42
|
-
sdist = { url = "https://files.pythonhosted.org/packages/50/79/66800aadf48771f6b62f7eb014e352e5d06856655206165d775e675a02c9/exceptiongroup-1.3.1.tar.gz", hash = "sha256:8b412432c6055b0b7d14c310000ae93352ed6754f70fa8f7c34141f91c4e3219", size = 30371, upload-time = "2025-11-21T23:01:54.787Z" }
|
|
43
|
-
wheels = [
|
|
44
|
-
{ url = "https://files.pythonhosted.org/packages/8a/0e/97c33bf5009bdbac74fd2beace167cab3f978feb69cc36f1ef79360d6c4e/exceptiongroup-1.3.1-py3-none-any.whl", hash = "sha256:a7a39a3bd276781e98394987d3a5701d0c4edffb633bb7a5144577f82c773598", size = 16740, upload-time = "2025-11-21T23:01:53.443Z" },
|
|
45
|
-
]
|
|
46
|
-
|
|
47
35
|
[[package]]
|
|
48
36
|
name = "foragefacebook"
|
|
49
|
-
version = "2.
|
|
37
|
+
version = "2.2.0"
|
|
50
38
|
source = { editable = "." }
|
|
51
39
|
dependencies = [
|
|
52
40
|
{ name = "click" },
|
|
@@ -57,7 +45,6 @@ dependencies = [
|
|
|
57
45
|
|
|
58
46
|
[package.optional-dependencies]
|
|
59
47
|
dev = [
|
|
60
|
-
{ name = "pytest" },
|
|
61
48
|
{ name = "ruff" },
|
|
62
49
|
{ name = "ty" },
|
|
63
50
|
]
|
|
@@ -67,7 +54,6 @@ requires-dist = [
|
|
|
67
54
|
{ name = "click", specifier = ">=8.0" },
|
|
68
55
|
{ name = "playwright", specifier = ">=1.40" },
|
|
69
56
|
{ name = "pydantic", specifier = ">=2.0" },
|
|
70
|
-
{ name = "pytest", marker = "extra == 'dev'", specifier = ">=8.0" },
|
|
71
57
|
{ name = "rich", specifier = ">=13.0" },
|
|
72
58
|
{ name = "ruff", marker = "extra == 'dev'", specifier = ">=0.8" },
|
|
73
59
|
{ name = "ty", marker = "extra == 'dev'", specifier = ">=0.0.1a6" },
|
|
@@ -129,15 +115,6 @@ wheels = [
|
|
|
129
115
|
{ url = "https://files.pythonhosted.org/packages/4f/dc/041be1dff9f23dac5f48a43323cd0789cb798342011c19a248d9c9335536/greenlet-3.3.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:6c10513330af5b8ae16f023e8ddbfb486ab355d04467c4679c5cfe4659975dd9", size = 1676034, upload-time = "2025-12-04T14:27:33.531Z" },
|
|
130
116
|
]
|
|
131
117
|
|
|
132
|
-
[[package]]
|
|
133
|
-
name = "iniconfig"
|
|
134
|
-
version = "2.3.0"
|
|
135
|
-
source = { registry = "https://pypi.org/simple" }
|
|
136
|
-
sdist = { url = "https://files.pythonhosted.org/packages/72/34/14ca021ce8e5dfedc35312d08ba8bf51fdd999c576889fc2c24cb97f4f10/iniconfig-2.3.0.tar.gz", hash = "sha256:c76315c77db068650d49c5b56314774a7804df16fee4402c1f19d6d15d8c4730", size = 20503, upload-time = "2025-10-18T21:55:43.219Z" }
|
|
137
|
-
wheels = [
|
|
138
|
-
{ url = "https://files.pythonhosted.org/packages/cb/b1/3846dd7f199d53cb17f49cba7e651e9ce294d8497c8c150530ed11865bb8/iniconfig-2.3.0-py3-none-any.whl", hash = "sha256:f631c04d2c48c52b84d0d0549c99ff3859c98df65b3101406327ecc7d53fbf12", size = 7484, upload-time = "2025-10-18T21:55:41.639Z" },
|
|
139
|
-
]
|
|
140
|
-
|
|
141
118
|
[[package]]
|
|
142
119
|
name = "markdown-it-py"
|
|
143
120
|
version = "4.0.0"
|
|
@@ -159,15 +136,6 @@ wheels = [
|
|
|
159
136
|
{ url = "https://files.pythonhosted.org/packages/b3/38/89ba8ad64ae25be8de66a6d463314cf1eb366222074cfda9ee839c56a4b4/mdurl-0.1.2-py3-none-any.whl", hash = "sha256:84008a41e51615a49fc9966191ff91509e3c40b939176e643fd50a5c2196b8f8", size = 9979, upload-time = "2022-08-14T12:40:09.779Z" },
|
|
160
137
|
]
|
|
161
138
|
|
|
162
|
-
[[package]]
|
|
163
|
-
name = "packaging"
|
|
164
|
-
version = "25.0"
|
|
165
|
-
source = { registry = "https://pypi.org/simple" }
|
|
166
|
-
sdist = { url = "https://files.pythonhosted.org/packages/a1/d4/1fc4078c65507b51b96ca8f8c3ba19e6a61c8253c72794544580a7b6c24d/packaging-25.0.tar.gz", hash = "sha256:d443872c98d677bf60f6a1f2f8c1cb748e8fe762d2bf9d3148b5599295b0fc4f", size = 165727, upload-time = "2025-04-19T11:48:59.673Z" }
|
|
167
|
-
wheels = [
|
|
168
|
-
{ url = "https://files.pythonhosted.org/packages/20/12/38679034af332785aac8774540895e234f4d07f7545804097de4b666afd8/packaging-25.0-py3-none-any.whl", hash = "sha256:29572ef2b1f17581046b3a2227d5c611fb25ec70ca1ba8554b24b0e69331a484", size = 66469, upload-time = "2025-04-19T11:48:57.875Z" },
|
|
169
|
-
]
|
|
170
|
-
|
|
171
139
|
[[package]]
|
|
172
140
|
name = "playwright"
|
|
173
141
|
version = "1.57.0"
|
|
@@ -187,15 +155,6 @@ wheels = [
|
|
|
187
155
|
{ url = "https://files.pythonhosted.org/packages/6a/60/fe31d7e6b8907789dcb0584f88be741ba388413e4fbce35f1eba4e3073de/playwright-1.57.0-py3-none-win_arm64.whl", hash = "sha256:5f065f5a133dbc15e6e7c71e7bc04f258195755b1c32a432b792e28338c8335e", size = 32837940, upload-time = "2025-12-09T08:06:42.268Z" },
|
|
188
156
|
]
|
|
189
157
|
|
|
190
|
-
[[package]]
|
|
191
|
-
name = "pluggy"
|
|
192
|
-
version = "1.6.0"
|
|
193
|
-
source = { registry = "https://pypi.org/simple" }
|
|
194
|
-
sdist = { url = "https://files.pythonhosted.org/packages/f9/e2/3e91f31a7d2b083fe6ef3fa267035b518369d9511ffab804f839851d2779/pluggy-1.6.0.tar.gz", hash = "sha256:7dcc130b76258d33b90f61b658791dede3486c3e6bfb003ee5c9bfb396dd22f3", size = 69412, upload-time = "2025-05-15T12:30:07.975Z" }
|
|
195
|
-
wheels = [
|
|
196
|
-
{ url = "https://files.pythonhosted.org/packages/54/20/4d324d65cc6d9205fabedc306948156824eb9f0ee1633355a8f7ec5c66bf/pluggy-1.6.0-py3-none-any.whl", hash = "sha256:e920276dd6813095e9377c0bc5566d94c932c33b27a3e3945d8389c374dd4746", size = 20538, upload-time = "2025-05-15T12:30:06.134Z" },
|
|
197
|
-
]
|
|
198
|
-
|
|
199
158
|
[[package]]
|
|
200
159
|
name = "pydantic"
|
|
201
160
|
version = "2.12.5"
|
|
@@ -350,24 +309,6 @@ wheels = [
|
|
|
350
309
|
{ url = "https://files.pythonhosted.org/packages/c7/21/705964c7812476f378728bdf590ca4b771ec72385c533964653c68e86bdc/pygments-2.19.2-py3-none-any.whl", hash = "sha256:86540386c03d588bb81d44bc3928634ff26449851e99741617ecb9037ee5ec0b", size = 1225217, upload-time = "2025-06-21T13:39:07.939Z" },
|
|
351
310
|
]
|
|
352
311
|
|
|
353
|
-
[[package]]
|
|
354
|
-
name = "pytest"
|
|
355
|
-
version = "9.0.2"
|
|
356
|
-
source = { registry = "https://pypi.org/simple" }
|
|
357
|
-
dependencies = [
|
|
358
|
-
{ name = "colorama", marker = "sys_platform == 'win32'" },
|
|
359
|
-
{ name = "exceptiongroup", marker = "python_full_version < '3.11'" },
|
|
360
|
-
{ name = "iniconfig" },
|
|
361
|
-
{ name = "packaging" },
|
|
362
|
-
{ name = "pluggy" },
|
|
363
|
-
{ name = "pygments" },
|
|
364
|
-
{ name = "tomli", marker = "python_full_version < '3.11'" },
|
|
365
|
-
]
|
|
366
|
-
sdist = { url = "https://files.pythonhosted.org/packages/d1/db/7ef3487e0fb0049ddb5ce41d3a49c235bf9ad299b6a25d5780a89f19230f/pytest-9.0.2.tar.gz", hash = "sha256:75186651a92bd89611d1d9fc20f0b4345fd827c41ccd5c299a868a05d70edf11", size = 1568901, upload-time = "2025-12-06T21:30:51.014Z" }
|
|
367
|
-
wheels = [
|
|
368
|
-
{ url = "https://files.pythonhosted.org/packages/3b/ab/b3226f0bd7cdcf710fbede2b3548584366da3b19b5021e74f5bde2a8fa3f/pytest-9.0.2-py3-none-any.whl", hash = "sha256:711ffd45bf766d5264d487b917733b453d917afd2b0ad65223959f59089f875b", size = 374801, upload-time = "2025-12-06T21:30:49.154Z" },
|
|
369
|
-
]
|
|
370
|
-
|
|
371
312
|
[[package]]
|
|
372
313
|
name = "rich"
|
|
373
314
|
version = "14.2.0"
|
|
@@ -406,55 +347,6 @@ wheels = [
|
|
|
406
347
|
{ url = "https://files.pythonhosted.org/packages/62/d5/bc97ff895ec35cf3925d4bd60f3b39d822f377a446906ec9bcc87405e59b/ruff-0.15.14-py3-none-win_arm64.whl", hash = "sha256:ff47b90a9ef6a40c9e2f3b479c1fb78531adf055b94c1eba0a7ba04b31951826", size = 11208607, upload-time = "2026-05-21T14:34:26.525Z" },
|
|
407
348
|
]
|
|
408
349
|
|
|
409
|
-
[[package]]
|
|
410
|
-
name = "tomli"
|
|
411
|
-
version = "2.3.0"
|
|
412
|
-
source = { registry = "https://pypi.org/simple" }
|
|
413
|
-
sdist = { url = "https://files.pythonhosted.org/packages/52/ed/3f73f72945444548f33eba9a87fc7a6e969915e7b1acc8260b30e1f76a2f/tomli-2.3.0.tar.gz", hash = "sha256:64be704a875d2a59753d80ee8a533c3fe183e3f06807ff7dc2232938ccb01549", size = 17392, upload-time = "2025-10-08T22:01:47.119Z" }
|
|
414
|
-
wheels = [
|
|
415
|
-
{ url = "https://files.pythonhosted.org/packages/b3/2e/299f62b401438d5fe1624119c723f5d877acc86a4c2492da405626665f12/tomli-2.3.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:88bd15eb972f3664f5ed4b57c1634a97153b4bac4479dcb6a495f41921eb7f45", size = 153236, upload-time = "2025-10-08T22:01:00.137Z" },
|
|
416
|
-
{ url = "https://files.pythonhosted.org/packages/86/7f/d8fffe6a7aefdb61bced88fcb5e280cfd71e08939da5894161bd71bea022/tomli-2.3.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:883b1c0d6398a6a9d29b508c331fa56adbcdff647f6ace4dfca0f50e90dfd0ba", size = 148084, upload-time = "2025-10-08T22:01:01.63Z" },
|
|
417
|
-
{ url = "https://files.pythonhosted.org/packages/47/5c/24935fb6a2ee63e86d80e4d3b58b222dafaf438c416752c8b58537c8b89a/tomli-2.3.0-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:d1381caf13ab9f300e30dd8feadb3de072aeb86f1d34a8569453ff32a7dea4bf", size = 234832, upload-time = "2025-10-08T22:01:02.543Z" },
|
|
418
|
-
{ url = "https://files.pythonhosted.org/packages/89/da/75dfd804fc11e6612846758a23f13271b76d577e299592b4371a4ca4cd09/tomli-2.3.0-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:a0e285d2649b78c0d9027570d4da3425bdb49830a6156121360b3f8511ea3441", size = 242052, upload-time = "2025-10-08T22:01:03.836Z" },
|
|
419
|
-
{ url = "https://files.pythonhosted.org/packages/70/8c/f48ac899f7b3ca7eb13af73bacbc93aec37f9c954df3c08ad96991c8c373/tomli-2.3.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:0a154a9ae14bfcf5d8917a59b51ffd5a3ac1fd149b71b47a3a104ca4edcfa845", size = 239555, upload-time = "2025-10-08T22:01:04.834Z" },
|
|
420
|
-
{ url = "https://files.pythonhosted.org/packages/ba/28/72f8afd73f1d0e7829bfc093f4cb98ce0a40ffc0cc997009ee1ed94ba705/tomli-2.3.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:74bf8464ff93e413514fefd2be591c3b0b23231a77f901db1eb30d6f712fc42c", size = 245128, upload-time = "2025-10-08T22:01:05.84Z" },
|
|
421
|
-
{ url = "https://files.pythonhosted.org/packages/b6/eb/a7679c8ac85208706d27436e8d421dfa39d4c914dcf5fa8083a9305f58d9/tomli-2.3.0-cp311-cp311-win32.whl", hash = "sha256:00b5f5d95bbfc7d12f91ad8c593a1659b6387b43f054104cda404be6bda62456", size = 96445, upload-time = "2025-10-08T22:01:06.896Z" },
|
|
422
|
-
{ url = "https://files.pythonhosted.org/packages/0a/fe/3d3420c4cb1ad9cb462fb52967080575f15898da97e21cb6f1361d505383/tomli-2.3.0-cp311-cp311-win_amd64.whl", hash = "sha256:4dc4ce8483a5d429ab602f111a93a6ab1ed425eae3122032db7e9acf449451be", size = 107165, upload-time = "2025-10-08T22:01:08.107Z" },
|
|
423
|
-
{ url = "https://files.pythonhosted.org/packages/ff/b7/40f36368fcabc518bb11c8f06379a0fd631985046c038aca08c6d6a43c6e/tomli-2.3.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:d7d86942e56ded512a594786a5ba0a5e521d02529b3826e7761a05138341a2ac", size = 154891, upload-time = "2025-10-08T22:01:09.082Z" },
|
|
424
|
-
{ url = "https://files.pythonhosted.org/packages/f9/3f/d9dd692199e3b3aab2e4e4dd948abd0f790d9ded8cd10cbaae276a898434/tomli-2.3.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:73ee0b47d4dad1c5e996e3cd33b8a76a50167ae5f96a2607cbe8cc773506ab22", size = 148796, upload-time = "2025-10-08T22:01:10.266Z" },
|
|
425
|
-
{ url = "https://files.pythonhosted.org/packages/60/83/59bff4996c2cf9f9387a0f5a3394629c7efa5ef16142076a23a90f1955fa/tomli-2.3.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:792262b94d5d0a466afb5bc63c7daa9d75520110971ee269152083270998316f", size = 242121, upload-time = "2025-10-08T22:01:11.332Z" },
|
|
426
|
-
{ url = "https://files.pythonhosted.org/packages/45/e5/7c5119ff39de8693d6baab6c0b6dcb556d192c165596e9fc231ea1052041/tomli-2.3.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:4f195fe57ecceac95a66a75ac24d9d5fbc98ef0962e09b2eddec5d39375aae52", size = 250070, upload-time = "2025-10-08T22:01:12.498Z" },
|
|
427
|
-
{ url = "https://files.pythonhosted.org/packages/45/12/ad5126d3a278f27e6701abde51d342aa78d06e27ce2bb596a01f7709a5a2/tomli-2.3.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:e31d432427dcbf4d86958c184b9bfd1e96b5b71f8eb17e6d02531f434fd335b8", size = 245859, upload-time = "2025-10-08T22:01:13.551Z" },
|
|
428
|
-
{ url = "https://files.pythonhosted.org/packages/fb/a1/4d6865da6a71c603cfe6ad0e6556c73c76548557a8d658f9e3b142df245f/tomli-2.3.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:7b0882799624980785240ab732537fcfc372601015c00f7fc367c55308c186f6", size = 250296, upload-time = "2025-10-08T22:01:14.614Z" },
|
|
429
|
-
{ url = "https://files.pythonhosted.org/packages/a0/b7/a7a7042715d55c9ba6e8b196d65d2cb662578b4d8cd17d882d45322b0d78/tomli-2.3.0-cp312-cp312-win32.whl", hash = "sha256:ff72b71b5d10d22ecb084d345fc26f42b5143c5533db5e2eaba7d2d335358876", size = 97124, upload-time = "2025-10-08T22:01:15.629Z" },
|
|
430
|
-
{ url = "https://files.pythonhosted.org/packages/06/1e/f22f100db15a68b520664eb3328fb0ae4e90530887928558112c8d1f4515/tomli-2.3.0-cp312-cp312-win_amd64.whl", hash = "sha256:1cb4ed918939151a03f33d4242ccd0aa5f11b3547d0cf30f7c74a408a5b99878", size = 107698, upload-time = "2025-10-08T22:01:16.51Z" },
|
|
431
|
-
{ url = "https://files.pythonhosted.org/packages/89/48/06ee6eabe4fdd9ecd48bf488f4ac783844fd777f547b8d1b61c11939974e/tomli-2.3.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:5192f562738228945d7b13d4930baffda67b69425a7f0da96d360b0a3888136b", size = 154819, upload-time = "2025-10-08T22:01:17.964Z" },
|
|
432
|
-
{ url = "https://files.pythonhosted.org/packages/f1/01/88793757d54d8937015c75dcdfb673c65471945f6be98e6a0410fba167ed/tomli-2.3.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:be71c93a63d738597996be9528f4abe628d1adf5e6eb11607bc8fe1a510b5dae", size = 148766, upload-time = "2025-10-08T22:01:18.959Z" },
|
|
433
|
-
{ url = "https://files.pythonhosted.org/packages/42/17/5e2c956f0144b812e7e107f94f1cc54af734eb17b5191c0bbfb72de5e93e/tomli-2.3.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c4665508bcbac83a31ff8ab08f424b665200c0e1e645d2bd9ab3d3e557b6185b", size = 240771, upload-time = "2025-10-08T22:01:20.106Z" },
|
|
434
|
-
{ url = "https://files.pythonhosted.org/packages/d5/f4/0fbd014909748706c01d16824eadb0307115f9562a15cbb012cd9b3512c5/tomli-2.3.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:4021923f97266babc6ccab9f5068642a0095faa0a51a246a6a02fccbb3514eaf", size = 248586, upload-time = "2025-10-08T22:01:21.164Z" },
|
|
435
|
-
{ url = "https://files.pythonhosted.org/packages/30/77/fed85e114bde5e81ecf9bc5da0cc69f2914b38f4708c80ae67d0c10180c5/tomli-2.3.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:a4ea38c40145a357d513bffad0ed869f13c1773716cf71ccaa83b0fa0cc4e42f", size = 244792, upload-time = "2025-10-08T22:01:22.417Z" },
|
|
436
|
-
{ url = "https://files.pythonhosted.org/packages/55/92/afed3d497f7c186dc71e6ee6d4fcb0acfa5f7d0a1a2878f8beae379ae0cc/tomli-2.3.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:ad805ea85eda330dbad64c7ea7a4556259665bdf9d2672f5dccc740eb9d3ca05", size = 248909, upload-time = "2025-10-08T22:01:23.859Z" },
|
|
437
|
-
{ url = "https://files.pythonhosted.org/packages/f8/84/ef50c51b5a9472e7265ce1ffc7f24cd4023d289e109f669bdb1553f6a7c2/tomli-2.3.0-cp313-cp313-win32.whl", hash = "sha256:97d5eec30149fd3294270e889b4234023f2c69747e555a27bd708828353ab606", size = 96946, upload-time = "2025-10-08T22:01:24.893Z" },
|
|
438
|
-
{ url = "https://files.pythonhosted.org/packages/b2/b7/718cd1da0884f281f95ccfa3a6cc572d30053cba64603f79d431d3c9b61b/tomli-2.3.0-cp313-cp313-win_amd64.whl", hash = "sha256:0c95ca56fbe89e065c6ead5b593ee64b84a26fca063b5d71a1122bf26e533999", size = 107705, upload-time = "2025-10-08T22:01:26.153Z" },
|
|
439
|
-
{ url = "https://files.pythonhosted.org/packages/19/94/aeafa14a52e16163008060506fcb6aa1949d13548d13752171a755c65611/tomli-2.3.0-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:cebc6fe843e0733ee827a282aca4999b596241195f43b4cc371d64fc6639da9e", size = 154244, upload-time = "2025-10-08T22:01:27.06Z" },
|
|
440
|
-
{ url = "https://files.pythonhosted.org/packages/db/e4/1e58409aa78eefa47ccd19779fc6f36787edbe7d4cd330eeeedb33a4515b/tomli-2.3.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:4c2ef0244c75aba9355561272009d934953817c49f47d768070c3c94355c2aa3", size = 148637, upload-time = "2025-10-08T22:01:28.059Z" },
|
|
441
|
-
{ url = "https://files.pythonhosted.org/packages/26/b6/d1eccb62f665e44359226811064596dd6a366ea1f985839c566cd61525ae/tomli-2.3.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c22a8bf253bacc0cf11f35ad9808b6cb75ada2631c2d97c971122583b129afbc", size = 241925, upload-time = "2025-10-08T22:01:29.066Z" },
|
|
442
|
-
{ url = "https://files.pythonhosted.org/packages/70/91/7cdab9a03e6d3d2bb11beae108da5bdc1c34bdeb06e21163482544ddcc90/tomli-2.3.0-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0eea8cc5c5e9f89c9b90c4896a8deefc74f518db5927d0e0e8d4a80953d774d0", size = 249045, upload-time = "2025-10-08T22:01:31.98Z" },
|
|
443
|
-
{ url = "https://files.pythonhosted.org/packages/15/1b/8c26874ed1f6e4f1fcfeb868db8a794cbe9f227299402db58cfcc858766c/tomli-2.3.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:b74a0e59ec5d15127acdabd75ea17726ac4c5178ae51b85bfe39c4f8a278e879", size = 245835, upload-time = "2025-10-08T22:01:32.989Z" },
|
|
444
|
-
{ url = "https://files.pythonhosted.org/packages/fd/42/8e3c6a9a4b1a1360c1a2a39f0b972cef2cc9ebd56025168c4137192a9321/tomli-2.3.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:b5870b50c9db823c595983571d1296a6ff3e1b88f734a4c8f6fc6188397de005", size = 253109, upload-time = "2025-10-08T22:01:34.052Z" },
|
|
445
|
-
{ url = "https://files.pythonhosted.org/packages/22/0c/b4da635000a71b5f80130937eeac12e686eefb376b8dee113b4a582bba42/tomli-2.3.0-cp314-cp314-win32.whl", hash = "sha256:feb0dacc61170ed7ab602d3d972a58f14ee3ee60494292d384649a3dc38ef463", size = 97930, upload-time = "2025-10-08T22:01:35.082Z" },
|
|
446
|
-
{ url = "https://files.pythonhosted.org/packages/b9/74/cb1abc870a418ae99cd5c9547d6bce30701a954e0e721821df483ef7223c/tomli-2.3.0-cp314-cp314-win_amd64.whl", hash = "sha256:b273fcbd7fc64dc3600c098e39136522650c49bca95df2d11cf3b626422392c8", size = 107964, upload-time = "2025-10-08T22:01:36.057Z" },
|
|
447
|
-
{ url = "https://files.pythonhosted.org/packages/54/78/5c46fff6432a712af9f792944f4fcd7067d8823157949f4e40c56b8b3c83/tomli-2.3.0-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:940d56ee0410fa17ee1f12b817b37a4d4e4dc4d27340863cc67236c74f582e77", size = 163065, upload-time = "2025-10-08T22:01:37.27Z" },
|
|
448
|
-
{ url = "https://files.pythonhosted.org/packages/39/67/f85d9bd23182f45eca8939cd2bc7050e1f90c41f4a2ecbbd5963a1d1c486/tomli-2.3.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:f85209946d1fe94416debbb88d00eb92ce9cd5266775424ff81bc959e001acaf", size = 159088, upload-time = "2025-10-08T22:01:38.235Z" },
|
|
449
|
-
{ url = "https://files.pythonhosted.org/packages/26/5a/4b546a0405b9cc0659b399f12b6adb750757baf04250b148d3c5059fc4eb/tomli-2.3.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:a56212bdcce682e56b0aaf79e869ba5d15a6163f88d5451cbde388d48b13f530", size = 268193, upload-time = "2025-10-08T22:01:39.712Z" },
|
|
450
|
-
{ url = "https://files.pythonhosted.org/packages/42/4f/2c12a72ae22cf7b59a7fe75b3465b7aba40ea9145d026ba41cb382075b0e/tomli-2.3.0-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c5f3ffd1e098dfc032d4d3af5c0ac64f6d286d98bc148698356847b80fa4de1b", size = 275488, upload-time = "2025-10-08T22:01:40.773Z" },
|
|
451
|
-
{ url = "https://files.pythonhosted.org/packages/92/04/a038d65dbe160c3aa5a624e93ad98111090f6804027d474ba9c37c8ae186/tomli-2.3.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:5e01decd096b1530d97d5d85cb4dff4af2d8347bd35686654a004f8dea20fc67", size = 272669, upload-time = "2025-10-08T22:01:41.824Z" },
|
|
452
|
-
{ url = "https://files.pythonhosted.org/packages/be/2f/8b7c60a9d1612a7cbc39ffcca4f21a73bf368a80fc25bccf8253e2563267/tomli-2.3.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:8a35dd0e643bb2610f156cca8db95d213a90015c11fee76c946aa62b7ae7e02f", size = 279709, upload-time = "2025-10-08T22:01:43.177Z" },
|
|
453
|
-
{ url = "https://files.pythonhosted.org/packages/7e/46/cc36c679f09f27ded940281c38607716c86cf8ba4a518d524e349c8b4874/tomli-2.3.0-cp314-cp314t-win32.whl", hash = "sha256:a1f7f282fe248311650081faafa5f4732bdbfef5d45fe3f2e702fbc6f2d496e0", size = 107563, upload-time = "2025-10-08T22:01:44.233Z" },
|
|
454
|
-
{ url = "https://files.pythonhosted.org/packages/84/ff/426ca8683cf7b753614480484f6437f568fd2fda2edbdf57a2d3d8b27a0b/tomli-2.3.0-cp314-cp314t-win_amd64.whl", hash = "sha256:70a251f8d4ba2d9ac2542eecf008b3c8a9fc5c3f9f02c56a9d7952612be2fdba", size = 119756, upload-time = "2025-10-08T22:01:45.234Z" },
|
|
455
|
-
{ url = "https://files.pythonhosted.org/packages/77/b8/0135fadc89e73be292b473cb820b4f5a08197779206b33191e801feeae40/tomli-2.3.0-py3-none-any.whl", hash = "sha256:e95b1af3c5b07d9e643909b5abbec77cd9f1217e6d0bca72b0234736b9fb1f1b", size = 14408, upload-time = "2025-10-08T22:01:46.04Z" },
|
|
456
|
-
]
|
|
457
|
-
|
|
458
350
|
[[package]]
|
|
459
351
|
name = "ty"
|
|
460
352
|
version = "0.0.10"
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
"""Tests for forage."""
|
|
@@ -1,43 +0,0 @@
|
|
|
1
|
-
"""Use Playwright's real DOM for selector fixtures, with network access disabled."""
|
|
2
|
-
|
|
3
|
-
from pathlib import Path
|
|
4
|
-
from unittest.mock import MagicMock
|
|
5
|
-
|
|
6
|
-
import pytest
|
|
7
|
-
from playwright.sync_api import sync_playwright
|
|
8
|
-
|
|
9
|
-
FIXTURES_DIR = Path(__file__).parent / "fixtures"
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
@pytest.fixture(scope="session")
|
|
13
|
-
def browser():
|
|
14
|
-
with sync_playwright() as playwright:
|
|
15
|
-
browser = playwright.chromium.launch()
|
|
16
|
-
yield browser
|
|
17
|
-
browser.close()
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
@pytest.fixture
|
|
21
|
-
def dom_page(browser):
|
|
22
|
-
context = browser.new_context()
|
|
23
|
-
context.route("**/*", lambda route: route.abort())
|
|
24
|
-
page = context.new_page()
|
|
25
|
-
yield page
|
|
26
|
-
context.close()
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
@pytest.fixture
|
|
30
|
-
def mock_page():
|
|
31
|
-
return MagicMock()
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
@pytest.fixture
|
|
35
|
-
def simple_post_element(dom_page):
|
|
36
|
-
dom_page.set_content((FIXTURES_DIR / "post_simple.html").read_text())
|
|
37
|
-
return dom_page.query_selector('[role="article"]')
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
@pytest.fixture
|
|
41
|
-
def simple_comment_element(dom_page):
|
|
42
|
-
dom_page.set_content((FIXTURES_DIR / "comment_simple.html").read_text())
|
|
43
|
-
return dom_page.query_selector('[role="article"]')
|
|
@@ -1,7 +0,0 @@
|
|
|
1
|
-
<!-- Simple comment mock -->
|
|
2
|
-
<div role="article">
|
|
3
|
-
<strong>Bob Wilson</strong>
|
|
4
|
-
<a href="https://www.facebook.com/bob.wilson" role="link">Bob Wilson</a>
|
|
5
|
-
<div dir="auto">Great recommendation! I love that place.</div>
|
|
6
|
-
<span aria-label="5 reactions">5</span>
|
|
7
|
-
</div>
|