toapi 2.1.3__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.
Files changed (64) hide show
  1. toapi-2.2.0/.github/workflows/ci.yml +29 -0
  2. toapi-2.2.0/.gitignore +105 -0
  3. toapi-2.2.0/.omc/project-memory.json +174 -0
  4. toapi-2.2.0/.omc/state/agent-replay-86181004-c476-471f-90d2-1c64e40fb749.jsonl +4 -0
  5. toapi-2.2.0/.omc/state/hud-stdin-cache.json +1 -0
  6. toapi-2.2.0/.omc/state/idle-notif-cooldown.json +5 -0
  7. toapi-2.2.0/.omc/state/last-tool-error.json +7 -0
  8. toapi-2.2.0/.omc/state/mission-state.json +79 -0
  9. toapi-2.2.0/.omc/state/sessions/86181004-c476-471f-90d2-1c64e40fb749/hud-state.json +6 -0
  10. toapi-2.2.0/.omc/state/subagent-tracking.json +26 -0
  11. toapi-2.2.0/.pre-commit-config.yaml +7 -0
  12. toapi-2.2.0/PKG-INFO +117 -0
  13. toapi-2.2.0/README.md +96 -0
  14. toapi-2.2.0/docs/CNAME +1 -0
  15. toapi-2.2.0/docs/about/contributing.md +62 -0
  16. toapi-2.2.0/docs/about/installation.md +86 -0
  17. toapi-2.2.0/docs/about/license.md +13 -0
  18. toapi-2.2.0/docs/about/release-notes.md +32 -0
  19. toapi-2.2.0/docs/articles/index.md +0 -0
  20. toapi-2.2.0/docs/articles/release.md +96 -0
  21. toapi-2.2.0/docs/diagram.png +0 -0
  22. toapi-2.2.0/docs/imgs/introducing-1.png +0 -0
  23. toapi-2.2.0/docs/imgs/introducing-2.png +0 -0
  24. toapi-2.2.0/docs/imgs/introducing-3.png +0 -0
  25. toapi-2.2.0/docs/imgs/introducing-4.png +0 -0
  26. toapi-2.2.0/docs/imgs/runinglog.png +0 -0
  27. toapi-2.2.0/docs/imgs/runningitems.png +0 -0
  28. toapi-2.2.0/docs/imgs/runningresult.png +0 -0
  29. toapi-2.2.0/docs/imgs/runningstatus.png +0 -0
  30. toapi-2.2.0/docs/imgs/step-0-1.png +0 -0
  31. toapi-2.2.0/docs/index.md +227 -0
  32. toapi-2.2.0/docs/logo.png +0 -0
  33. toapi-2.2.0/docs/quickstart.md +0 -0
  34. toapi-2.2.0/docs/topics/api.md +90 -0
  35. toapi-2.2.0/docs/topics/cache.md +136 -0
  36. toapi-2.2.0/docs/topics/item.md +45 -0
  37. toapi-2.2.0/docs/topics/selector.md +57 -0
  38. toapi-2.2.0/docs/topics/settings.md +48 -0
  39. toapi-2.2.0/docs/topics/storage.md +67 -0
  40. toapi-2.2.0/docs/tutorials/introducing.md +28 -0
  41. toapi-2.2.0/docs/tutorials/step0-creating-new-project.md +41 -0
  42. toapi-2.2.0/docs/tutorials/step1-global-settings.md +39 -0
  43. toapi-2.2.0/docs/tutorials/step2-redis.md +48 -0
  44. toapi-2.2.0/docs/tutorials/step3-sqlite3.md +50 -0
  45. toapi-2.2.0/docs/tutorials/step4-defining-items.md +61 -0
  46. toapi-2.2.0/docs/tutorials/step5-deploy.md +95 -0
  47. toapi-2.2.0/examples/click/app.py +14 -0
  48. toapi-2.2.0/examples/click/static/main.js +0 -0
  49. toapi-2.2.0/examples/click/templates/index.html +75 -0
  50. toapi-2.2.0/examples/hackernews_page.py +32 -0
  51. toapi-2.2.0/mkdocs.yml +56 -0
  52. toapi-2.2.0/pyproject.toml +58 -0
  53. toapi-2.2.0/tests/test_toapi.py +55 -0
  54. toapi-2.2.0/toapi/__init__.py +7 -0
  55. {toapi-2.1.3 → toapi-2.2.0}/toapi/api.py +4 -7
  56. {toapi-2.1.3 → toapi-2.2.0}/toapi/item.py +1 -1
  57. {toapi-2.1.3 → toapi-2.2.0}/toapi/log.py +4 -11
  58. toapi-2.2.0/uv.lock +1434 -0
  59. toapi-2.1.3/PKG-INFO +0 -23
  60. toapi-2.1.3/pyproject.toml +0 -67
  61. toapi-2.1.3/setup.py +0 -41
  62. toapi-2.1.3/toapi/__init__.py +0 -2
  63. {toapi-2.1.3 → toapi-2.2.0}/LICENSE +0 -0
  64. {toapi-2.1.3 → toapi-2.2.0}/toapi/cli.py +0 -0
@@ -0,0 +1,29 @@
1
+ name: CI
2
+
3
+ on:
4
+ push:
5
+ branches: [master]
6
+ pull_request:
7
+
8
+ jobs:
9
+ test:
10
+ runs-on: ubuntu-latest
11
+ strategy:
12
+ fail-fast: false
13
+ matrix:
14
+ python: ["3.10", "3.11", "3.12"]
15
+ steps:
16
+ - uses: actions/checkout@v4
17
+ - uses: astral-sh/setup-uv@v3
18
+ with:
19
+ enable-cache: true
20
+ - name: Set up Python
21
+ run: uv python install ${{ matrix.python }}
22
+ - name: Install dependencies
23
+ run: uv sync --all-extras --dev
24
+ - name: Lint
25
+ run: |
26
+ uv run ruff check .
27
+ uv run ruff format --check .
28
+ - name: Test
29
+ run: uv run pytest --cov=toapi
toapi-2.2.0/.gitignore ADDED
@@ -0,0 +1,105 @@
1
+ .idea/
2
+ # Byte-compiled / optimized / DLL files
3
+ __pycache__/
4
+ *.py[cod]
5
+ *$py.class
6
+ .html/
7
+ # C extensions
8
+ *.so
9
+ .pytest_cache/
10
+ # Distribution / packaging
11
+ .Python
12
+ env/
13
+ env27/
14
+ build/
15
+ develop-eggs/
16
+ dist/
17
+ downloads/
18
+ eggs/
19
+ .eggs/
20
+ lib/
21
+ lib64/
22
+ parts/
23
+ sdist/
24
+ var/
25
+ wheels/
26
+ *.egg-info/
27
+ .installed.cfg
28
+ *.egg
29
+
30
+ # PyInstaller
31
+ # Usually these files are written by a python script from a template
32
+ # before PyInstaller builds the exe, so as to inject date/other infos into it.
33
+ *.manifest
34
+ *.spec
35
+
36
+ # Installer logs
37
+ pip-log.txt
38
+ pip-delete-this-directory.txt
39
+
40
+ # Unit test / coverage reports
41
+ htmlcov/
42
+ .tox/
43
+ .coverage
44
+ .coverage.*
45
+ .cache
46
+ nosetests.xml
47
+ coverage.xml
48
+ *.cover
49
+ .hypothesis/
50
+
51
+ # Translations
52
+ *.mo
53
+ *.pot
54
+
55
+ # Django stuff:
56
+ *.log
57
+ local_settings.py
58
+
59
+ # Flask stuff:
60
+ instance/
61
+ .webassets-cache
62
+
63
+ # Scrapy stuff:
64
+ .scrapy
65
+
66
+ # Sphinx documentation
67
+ docs/_build/
68
+
69
+ # PyBuilder
70
+ target/
71
+
72
+ # Jupyter Notebook
73
+ .ipynb_checkpoints
74
+
75
+ # pyenv
76
+ .python-version
77
+
78
+ # celery beat schedule file
79
+ celerybeat-schedule
80
+
81
+ # SageMath parsed files
82
+ *.sage.py
83
+
84
+ # dotenv
85
+ .env
86
+
87
+ # virtualenv
88
+ .venv
89
+ venv/
90
+ ENV/
91
+
92
+ # Spyder project settings
93
+ .spyderproject
94
+ .spyproject
95
+
96
+ # Rope project settings
97
+ .ropeproject
98
+
99
+ # mkdocs documentation
100
+ /site
101
+
102
+ # mypy
103
+ .mypy_cache/
104
+ /tests/data.sqlite
105
+ /examples/toapi-pic/data.sqlite
@@ -0,0 +1,174 @@
1
+ {
2
+ "version": "1.0.0",
3
+ "lastScanned": 1779434703584,
4
+ "projectRoot": "/Users/elliot/Documents/Code/toapi",
5
+ "techStack": {
6
+ "languages": [
7
+ {
8
+ "name": "Python",
9
+ "version": "^3.8",
10
+ "confidence": "high",
11
+ "markers": [
12
+ "pyproject.toml"
13
+ ]
14
+ }
15
+ ],
16
+ "frameworks": [],
17
+ "packageManager": "poetry",
18
+ "runtime": null
19
+ },
20
+ "build": {
21
+ "buildCommand": null,
22
+ "testCommand": "pytest",
23
+ "lintCommand": "ruff check",
24
+ "devCommand": null,
25
+ "scripts": {}
26
+ },
27
+ "conventions": {
28
+ "namingStyle": null,
29
+ "importStyle": null,
30
+ "testPattern": null,
31
+ "fileOrganization": null
32
+ },
33
+ "structure": {
34
+ "isMonorepo": false,
35
+ "workspaces": [],
36
+ "mainDirectories": [
37
+ "docs",
38
+ "examples",
39
+ "tests"
40
+ ],
41
+ "gitBranches": {
42
+ "defaultBranch": "master",
43
+ "branchingStrategy": null
44
+ }
45
+ },
46
+ "customNotes": [],
47
+ "directoryMap": {
48
+ "docs": {
49
+ "path": "docs",
50
+ "purpose": "Documentation",
51
+ "fileCount": 5,
52
+ "lastAccessed": 1779434703569,
53
+ "keyFiles": [
54
+ "CNAME",
55
+ "diagram.png",
56
+ "index.md",
57
+ "logo.png",
58
+ "quickstart.md"
59
+ ]
60
+ },
61
+ "examples": {
62
+ "path": "examples",
63
+ "purpose": "Example code",
64
+ "fileCount": 1,
65
+ "lastAccessed": 1779434703569,
66
+ "keyFiles": [
67
+ "hackernews_page.py"
68
+ ]
69
+ },
70
+ "tests": {
71
+ "path": "tests",
72
+ "purpose": "Test files",
73
+ "fileCount": 1,
74
+ "lastAccessed": 1779434703569,
75
+ "keyFiles": [
76
+ "test_toapi.py"
77
+ ]
78
+ },
79
+ "toapi": {
80
+ "path": "toapi",
81
+ "purpose": null,
82
+ "fileCount": 5,
83
+ "lastAccessed": 1779434703570,
84
+ "keyFiles": [
85
+ "__init__.py",
86
+ "api.py",
87
+ "cli.py",
88
+ "item.py",
89
+ "log.py"
90
+ ]
91
+ }
92
+ },
93
+ "hotPaths": [
94
+ {
95
+ "path": "pyproject.toml",
96
+ "accessCount": 7,
97
+ "lastAccessed": 1779435734082,
98
+ "type": "file"
99
+ },
100
+ {
101
+ "path": "toapi/api.py",
102
+ "accessCount": 4,
103
+ "lastAccessed": 1779435005253,
104
+ "type": "file"
105
+ },
106
+ {
107
+ "path": "toapi/__init__.py",
108
+ "accessCount": 4,
109
+ "lastAccessed": 1779435605203,
110
+ "type": "file"
111
+ },
112
+ {
113
+ "path": ".pre-commit-config.yaml",
114
+ "accessCount": 3,
115
+ "lastAccessed": 1779434999306,
116
+ "type": "file"
117
+ },
118
+ {
119
+ "path": "toapi/item.py",
120
+ "accessCount": 3,
121
+ "lastAccessed": 1779435568476,
122
+ "type": "file"
123
+ },
124
+ {
125
+ "path": ".travis.yml",
126
+ "accessCount": 2,
127
+ "lastAccessed": 1779434902331,
128
+ "type": "file"
129
+ },
130
+ {
131
+ "path": ".flake8",
132
+ "accessCount": 2,
133
+ "lastAccessed": 1779434903048,
134
+ "type": "file"
135
+ },
136
+ {
137
+ "path": "toapi/cli.py",
138
+ "accessCount": 2,
139
+ "lastAccessed": 1779435596581,
140
+ "type": "file"
141
+ },
142
+ {
143
+ "path": "README.md",
144
+ "accessCount": 1,
145
+ "lastAccessed": 1779434789416,
146
+ "type": "file"
147
+ },
148
+ {
149
+ "path": "toapi/log.py",
150
+ "accessCount": 1,
151
+ "lastAccessed": 1779434808975,
152
+ "type": "file"
153
+ },
154
+ {
155
+ "path": "poetry.lock",
156
+ "accessCount": 1,
157
+ "lastAccessed": 1779434809861,
158
+ "type": "file"
159
+ },
160
+ {
161
+ "path": "tests/test_toapi.py",
162
+ "accessCount": 1,
163
+ "lastAccessed": 1779434815385,
164
+ "type": "file"
165
+ },
166
+ {
167
+ "path": "LICENSE",
168
+ "accessCount": 1,
169
+ "lastAccessed": 1779434833764,
170
+ "type": "file"
171
+ }
172
+ ],
173
+ "userDirectives": []
174
+ }
@@ -0,0 +1,4 @@
1
+ {"t":0,"agent":"aae2d16","agent_type":"Explore","event":"agent_start","parent_mode":"none"}
2
+ {"t":0,"agent":"a324b3e","agent_type":"Explore","event":"agent_start","parent_mode":"none"}
3
+ {"t":0,"agent":"a324b3e","agent_type":"Explore","event":"agent_stop","success":true,"duration_ms":87042}
4
+ {"t":0,"agent":"aae2d16","agent_type":"Explore","event":"agent_stop","success":true,"duration_ms":105179}
@@ -0,0 +1 @@
1
+ {"session_id":"86181004-c476-471f-90d2-1c64e40fb749","transcript_path":"/Users/elliot/.claude/projects/-Users-elliot-Documents-Code-toapi/86181004-c476-471f-90d2-1c64e40fb749.jsonl","cwd":"/Users/elliot/Documents/Code/toapi","effort":{"level":"xhigh"},"session_name":"modernize-toapi-dependencies","model":{"id":"claude-opus-4-7[1m]","display_name":"Opus 4.7 (1M context)"},"workspace":{"current_dir":"/Users/elliot/Documents/Code/toapi","project_dir":"/Users/elliot/Documents/Code/toapi","added_dirs":[]},"version":"2.1.144","output_style":{"name":"default"},"cost":{"total_cost_usd":3.1557453,"total_duration_ms":1033890,"total_api_duration_ms":569612,"total_lines_added":260,"total_lines_removed":99},"context_window":{"total_input_tokens":82733,"total_output_tokens":961,"context_window_size":1000000,"current_usage":{"input_tokens":6,"output_tokens":961,"cache_creation_input_tokens":1867,"cache_read_input_tokens":80860},"used_percentage":8,"remaining_percentage":92},"exceeds_200k_tokens":false,"fast_mode":false,"thinking":{"enabled":true},"rate_limits":{"five_hour":{"used_percentage":34,"resets_at":1779440400},"seven_day":{"used_percentage":80,"resets_at":1779570000}}}
@@ -0,0 +1,5 @@
1
+ {
2
+ "lastSentAt": "2026-05-22T07:40:42.602Z",
3
+ "repoSignature": "{\"repo\":\"elliotgao2/toapi\",\"headSha\":\"6ae043cab28d16beb0be1bd9b1cd0fdc9c19baa6\",\"dirty\":true,\"openPrNumbers\":[127,135,138],\"openIssueNumbers\":[129,130,131,132,134,139],\"failingRunIds\":[24379413758,25783406210]}",
4
+ "backlogZero": false
5
+ }
@@ -0,0 +1,7 @@
1
+ {
2
+ "tool_name": "Bash",
3
+ "tool_input_preview": "{\"command\":\"ls /opt/homebrew/bin/python3* /usr/local/bin/python3* /opt/nanobrew/prefix/bin/python3* 2>/dev/null; echo \\\"---\\\"; ls /opt/homebrew/opt 2>/dev/null | grep -i python; echo \\\"---\\\"; which py...",
4
+ "error": "Exit code 1\n/opt/homebrew/bin/python3\n/opt/homebrew/bin/python3-config\n/opt/homebrew/bin/python3.13\n/opt/homebrew/bin/python3.13-config\n/opt/homebrew/bin/python3.14\n/opt/homebrew/bin/python3.14-config\n/opt/nanobrew/prefix/bin/python3\n/opt/nanobrew/prefix/bin/python3-config\n/opt/nanobrew/prefix/bin/python3.13\n/opt/nanobrew/prefix/bin/python3.13-config\n/opt/nanobrew/prefix/bin/python3.14\n/opt/nanobrew/prefix/bin/python3.14-config\n/usr/local/bin/python3\n/usr/local/bin/python3-config\n/usr/local/bin/...",
5
+ "timestamp": "2026-05-22T07:30:24.107Z",
6
+ "retry_count": 2
7
+ }
@@ -0,0 +1,79 @@
1
+ {
2
+ "updatedAt": "2026-05-22T07:27:37.778Z",
3
+ "missions": [
4
+ {
5
+ "id": "session:86181004-c476-471f-90d2-1c64e40fb749:none",
6
+ "source": "session",
7
+ "name": "none",
8
+ "objective": "Session mission",
9
+ "createdAt": "2026-05-22T07:25:52.599Z",
10
+ "updatedAt": "2026-05-22T07:27:37.778Z",
11
+ "status": "done",
12
+ "workerCount": 2,
13
+ "taskCounts": {
14
+ "total": 2,
15
+ "pending": 0,
16
+ "blocked": 0,
17
+ "inProgress": 0,
18
+ "completed": 2,
19
+ "failed": 0
20
+ },
21
+ "agents": [
22
+ {
23
+ "name": "Explore:aae2d16",
24
+ "role": "Explore",
25
+ "ownership": "aae2d161a0ef7e8e0",
26
+ "status": "done",
27
+ "currentStep": null,
28
+ "latestUpdate": "completed",
29
+ "completedSummary": null,
30
+ "updatedAt": "2026-05-22T07:27:37.778Z"
31
+ },
32
+ {
33
+ "name": "Explore:a324b3e",
34
+ "role": "Explore",
35
+ "ownership": "a324b3eaed6783abe",
36
+ "status": "done",
37
+ "currentStep": null,
38
+ "latestUpdate": "completed",
39
+ "completedSummary": null,
40
+ "updatedAt": "2026-05-22T07:27:37.526Z"
41
+ }
42
+ ],
43
+ "timeline": [
44
+ {
45
+ "id": "session-start:aae2d161a0ef7e8e0:2026-05-22T07:25:52.599Z",
46
+ "at": "2026-05-22T07:25:52.599Z",
47
+ "kind": "update",
48
+ "agent": "Explore:aae2d16",
49
+ "detail": "started Explore:aae2d16",
50
+ "sourceKey": "session-start:aae2d161a0ef7e8e0"
51
+ },
52
+ {
53
+ "id": "session-start:a324b3eaed6783abe:2026-05-22T07:26:10.484Z",
54
+ "at": "2026-05-22T07:26:10.484Z",
55
+ "kind": "update",
56
+ "agent": "Explore:a324b3e",
57
+ "detail": "started Explore:a324b3e",
58
+ "sourceKey": "session-start:a324b3eaed6783abe"
59
+ },
60
+ {
61
+ "id": "session-stop:a324b3eaed6783abe:2026-05-22T07:27:37.526Z",
62
+ "at": "2026-05-22T07:27:37.526Z",
63
+ "kind": "completion",
64
+ "agent": "Explore:a324b3e",
65
+ "detail": "completed",
66
+ "sourceKey": "session-stop:a324b3eaed6783abe"
67
+ },
68
+ {
69
+ "id": "session-stop:aae2d161a0ef7e8e0:2026-05-22T07:27:37.778Z",
70
+ "at": "2026-05-22T07:27:37.778Z",
71
+ "kind": "completion",
72
+ "agent": "Explore:aae2d16",
73
+ "detail": "completed",
74
+ "sourceKey": "session-stop:aae2d161a0ef7e8e0"
75
+ }
76
+ ]
77
+ }
78
+ ]
79
+ }
@@ -0,0 +1,6 @@
1
+ {
2
+ "timestamp": "2026-05-22T07:25:44.780Z",
3
+ "backgroundTasks": [],
4
+ "sessionStartTimestamp": "2026-05-22T07:25:03.600Z",
5
+ "sessionId": "86181004-c476-471f-90d2-1c64e40fb749"
6
+ }
@@ -0,0 +1,26 @@
1
+ {
2
+ "agents": [
3
+ {
4
+ "agent_id": "aae2d161a0ef7e8e0",
5
+ "agent_type": "Explore",
6
+ "started_at": "2026-05-22T07:25:52.599Z",
7
+ "parent_mode": "none",
8
+ "status": "completed",
9
+ "completed_at": "2026-05-22T07:27:37.778Z",
10
+ "duration_ms": 105179
11
+ },
12
+ {
13
+ "agent_id": "a324b3eaed6783abe",
14
+ "agent_type": "Explore",
15
+ "started_at": "2026-05-22T07:26:10.484Z",
16
+ "parent_mode": "none",
17
+ "status": "completed",
18
+ "completed_at": "2026-05-22T07:27:37.526Z",
19
+ "duration_ms": 87042
20
+ }
21
+ ],
22
+ "total_spawned": 2,
23
+ "total_completed": 2,
24
+ "total_failed": 0,
25
+ "last_updated": "2026-05-22T07:27:37.880Z"
26
+ }
@@ -0,0 +1,7 @@
1
+ repos:
2
+ - repo: https://github.com/astral-sh/ruff-pre-commit
3
+ rev: v0.6.9
4
+ hooks:
5
+ - id: ruff
6
+ args: [--fix]
7
+ - id: ruff-format
toapi-2.2.0/PKG-INFO ADDED
@@ -0,0 +1,117 @@
1
+ Metadata-Version: 2.4
2
+ Name: toapi
3
+ Version: 2.2.0
4
+ Summary: Every web site provides APIs.
5
+ Project-URL: homepage, https://github.com/gaojiuli/toapi
6
+ Project-URL: repository, https://github.com/gaojiuli/toapi
7
+ Project-URL: documentation, https://gaojiuli.github.io/toapi/
8
+ Author-email: Elliot Gao <gaojiuli@gmail.com>
9
+ License: MIT
10
+ License-File: LICENSE
11
+ Requires-Python: >=3.10
12
+ Requires-Dist: charset-normalizer>=3.3
13
+ Requires-Dist: click>=8.1
14
+ Requires-Dist: colorama>=0.4.6
15
+ Requires-Dist: cssselect>=1.2
16
+ Requires-Dist: flask>=3.0
17
+ Requires-Dist: htmlfetcher>=0.0.6
18
+ Requires-Dist: htmlparsing>=0.1.5
19
+ Requires-Dist: requests>=2.32
20
+ Description-Content-Type: text/markdown
21
+
22
+ # Toapi
23
+
24
+ [![Build](https://travis-ci.org/gaojiuli/toapi.svg?branch=master)](https://travis-ci.org/gaojiuli/toapi)
25
+ [![Coverage](https://codecov.io/gh/gaojiuli/toapi/branch/master/graph/badge.svg)](https://codecov.io/gh/gaojiuli/toapi)
26
+ [![Python](https://img.shields.io/pypi/pyversions/toapi.svg)](https://pypi.python.org/pypi/toapi/)
27
+ [![Version](https://img.shields.io/pypi/v/toapi.svg)](https://pypi.python.org/pypi/toapi/)
28
+ [![License](https://img.shields.io/pypi/l/toapi.svg)](https://pypi.python.org/pypi/toapi/)
29
+
30
+ ## Overview
31
+
32
+ Toapi give you the ability to make every web site provides APIs.
33
+
34
+ - v1.0.0 Documentation: [http://www.toapi.org](http://www.toapi.org)
35
+ - Awesome: [https://github.com/toapi/awesome-toapi](https://github.com/toapi/awesome-toapi)
36
+ - Organization: [https://github.com/toapi](https://github.com/toapi)
37
+
38
+ ## Features
39
+
40
+ - Automatic converting HTML web site to API service.
41
+ - Automatic caching every page of source site.
42
+ - Automatic caching every request.
43
+ - Support merging multiple web sites into one API service.
44
+
45
+ ## Get Started
46
+
47
+ ### Installation
48
+
49
+ ```text
50
+ $ pip install toapi
51
+ ```
52
+
53
+ ### Usage
54
+
55
+ create `app.py` and copy the code:
56
+
57
+ ```python
58
+ from flask import request
59
+ from htmlparsing import Attr, Text
60
+ from toapi import Api, Item
61
+
62
+ api = Api()
63
+
64
+
65
+ @api.site('https://news.ycombinator.com')
66
+ @api.list('.athing')
67
+ @api.route('/posts?page={page}', '/news?p={page}')
68
+ @api.route('/posts', '/news?p=1')
69
+ class Post(Item):
70
+ url = Attr('.storylink', 'href')
71
+ title = Text('.storylink')
72
+
73
+
74
+ @api.site('https://news.ycombinator.com')
75
+ @api.route('/posts?page={page}', '/news?p={page}')
76
+ @api.route('/posts', '/news?p=1')
77
+ class Page(Item):
78
+ next_page = Attr('.morelink', 'href')
79
+
80
+ def clean_next_page(self, value):
81
+ return api.convert_string('/' + value, '/news?p={page}', request.host_url.strip('/') + '/posts?page={page}')
82
+
83
+
84
+ api.run(debug=True, host='0.0.0.0', port=5000)
85
+ ```
86
+
87
+ run `python app.py`
88
+
89
+ then open your browser and visit `http://127.0.0.1:5000/posts?page=1`
90
+
91
+ you will get the result like:
92
+
93
+ ```json
94
+ {
95
+ "Page": {
96
+ "next_page": "http://127.0.0.1:5000/posts?page=2"
97
+ },
98
+ "Post": [
99
+ {
100
+ "title": "Mathematicians Crack the Cursed Curve",
101
+ "url": "https://www.quantamagazine.org/mathematicians-crack-the-cursed-curve-20171207/"
102
+ },
103
+ {
104
+ "title": "Stuffing a Tesla Drivetrain into a 1981 Honda Accord",
105
+ "url": "https://jalopnik.com/this-glorious-madman-stuffed-a-p85-tesla-drivetrain-int-1823461909"
106
+ }
107
+ ]
108
+ }
109
+ ```
110
+
111
+
112
+ ## Contributing
113
+
114
+ Write code and test code and pull request.
115
+
116
+
117
+
toapi-2.2.0/README.md ADDED
@@ -0,0 +1,96 @@
1
+ # Toapi
2
+
3
+ [![Build](https://travis-ci.org/gaojiuli/toapi.svg?branch=master)](https://travis-ci.org/gaojiuli/toapi)
4
+ [![Coverage](https://codecov.io/gh/gaojiuli/toapi/branch/master/graph/badge.svg)](https://codecov.io/gh/gaojiuli/toapi)
5
+ [![Python](https://img.shields.io/pypi/pyversions/toapi.svg)](https://pypi.python.org/pypi/toapi/)
6
+ [![Version](https://img.shields.io/pypi/v/toapi.svg)](https://pypi.python.org/pypi/toapi/)
7
+ [![License](https://img.shields.io/pypi/l/toapi.svg)](https://pypi.python.org/pypi/toapi/)
8
+
9
+ ## Overview
10
+
11
+ Toapi give you the ability to make every web site provides APIs.
12
+
13
+ - v1.0.0 Documentation: [http://www.toapi.org](http://www.toapi.org)
14
+ - Awesome: [https://github.com/toapi/awesome-toapi](https://github.com/toapi/awesome-toapi)
15
+ - Organization: [https://github.com/toapi](https://github.com/toapi)
16
+
17
+ ## Features
18
+
19
+ - Automatic converting HTML web site to API service.
20
+ - Automatic caching every page of source site.
21
+ - Automatic caching every request.
22
+ - Support merging multiple web sites into one API service.
23
+
24
+ ## Get Started
25
+
26
+ ### Installation
27
+
28
+ ```text
29
+ $ pip install toapi
30
+ ```
31
+
32
+ ### Usage
33
+
34
+ create `app.py` and copy the code:
35
+
36
+ ```python
37
+ from flask import request
38
+ from htmlparsing import Attr, Text
39
+ from toapi import Api, Item
40
+
41
+ api = Api()
42
+
43
+
44
+ @api.site('https://news.ycombinator.com')
45
+ @api.list('.athing')
46
+ @api.route('/posts?page={page}', '/news?p={page}')
47
+ @api.route('/posts', '/news?p=1')
48
+ class Post(Item):
49
+ url = Attr('.storylink', 'href')
50
+ title = Text('.storylink')
51
+
52
+
53
+ @api.site('https://news.ycombinator.com')
54
+ @api.route('/posts?page={page}', '/news?p={page}')
55
+ @api.route('/posts', '/news?p=1')
56
+ class Page(Item):
57
+ next_page = Attr('.morelink', 'href')
58
+
59
+ def clean_next_page(self, value):
60
+ return api.convert_string('/' + value, '/news?p={page}', request.host_url.strip('/') + '/posts?page={page}')
61
+
62
+
63
+ api.run(debug=True, host='0.0.0.0', port=5000)
64
+ ```
65
+
66
+ run `python app.py`
67
+
68
+ then open your browser and visit `http://127.0.0.1:5000/posts?page=1`
69
+
70
+ you will get the result like:
71
+
72
+ ```json
73
+ {
74
+ "Page": {
75
+ "next_page": "http://127.0.0.1:5000/posts?page=2"
76
+ },
77
+ "Post": [
78
+ {
79
+ "title": "Mathematicians Crack the Cursed Curve",
80
+ "url": "https://www.quantamagazine.org/mathematicians-crack-the-cursed-curve-20171207/"
81
+ },
82
+ {
83
+ "title": "Stuffing a Tesla Drivetrain into a 1981 Honda Accord",
84
+ "url": "https://jalopnik.com/this-glorious-madman-stuffed-a-p85-tesla-drivetrain-int-1823461909"
85
+ }
86
+ ]
87
+ }
88
+ ```
89
+
90
+
91
+ ## Contributing
92
+
93
+ Write code and test code and pull request.
94
+
95
+
96
+
toapi-2.2.0/docs/CNAME ADDED
@@ -0,0 +1 @@
1
+ www.toapi.org