artydev commited on
Commit
57b35b1
·
verified ·
1 Parent(s): 3f458b9

deploy at 2024-08-30 23:55:04.515882

Browse files
Files changed (10) hide show
  1. .gitignore +147 -0
  2. Dockerfile +10 -0
  3. LICENSE +201 -0
  4. README.md +29 -10
  5. fasthtml_hf/__init__.py +2 -0
  6. fasthtml_hf/backup.py +49 -0
  7. fasthtml_hf/deploy.py +74 -0
  8. main.py +18 -0
  9. pyproject.toml +28 -0
  10. requirements.txt +2 -0
.gitignore ADDED
@@ -0,0 +1,147 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ _proc/
2
+ _quarto.yml
3
+ sidebar.yml
4
+ Gemfile.lock
5
+ token
6
+ _docs/
7
+ conda/
8
+ .last_checked
9
+ .gitconfig
10
+ *.bak
11
+ *.log
12
+ *~
13
+ ~*
14
+ _tmp*
15
+ tmp*
16
+ tags
17
+
18
+ # Byte-compiled / optimized / DLL files
19
+ __pycache__/
20
+ *.py[cod]
21
+ *$py.class
22
+
23
+ # C extensions
24
+ *.so
25
+
26
+ # Distribution / packaging
27
+ .Python
28
+ env/
29
+ build/
30
+ develop-eggs/
31
+ dist/
32
+ downloads/
33
+ eggs/
34
+ .eggs/
35
+ lib/
36
+ lib64/
37
+ parts/
38
+ sdist/
39
+ var/
40
+ wheels/
41
+ *.egg-info/
42
+ .installed.cfg
43
+ *.egg
44
+
45
+ # PyInstaller
46
+ # Usually these files are written by a python script from a template
47
+ # before PyInstaller builds the exe, so as to inject date/other infos into it.
48
+ *.manifest
49
+ *.spec
50
+
51
+ # Installer logs
52
+ pip-log.txt
53
+ pip-delete-this-directory.txt
54
+
55
+ # Unit test / coverage reports
56
+ htmlcov/
57
+ .tox/
58
+ .coverage
59
+ .coverage.*
60
+ .cache
61
+ nosetests.xml
62
+ coverage.xml
63
+ *.cover
64
+ .hypothesis/
65
+
66
+ # Translations
67
+ *.mo
68
+ *.pot
69
+
70
+ # Django stuff:
71
+ *.log
72
+ local_settings.py
73
+
74
+ # Flask stuff:
75
+ instance/
76
+ .webassets-cache
77
+
78
+ # Scrapy stuff:
79
+ .scrapy
80
+
81
+ # Sphinx documentation
82
+ docs/_build/
83
+
84
+ # PyBuilder
85
+ target/
86
+
87
+ # Jupyter Notebook
88
+ .ipynb_checkpoints
89
+
90
+ # pyenv
91
+ .python-version
92
+
93
+ # celery beat schedule file
94
+ celerybeat-schedule
95
+
96
+ # SageMath parsed files
97
+ *.sage.py
98
+
99
+ # dotenv
100
+ .env
101
+
102
+ # virtualenv
103
+ .venv
104
+ venv/
105
+ ENV/
106
+
107
+ # Spyder project settings
108
+ .spyderproject
109
+ .spyproject
110
+
111
+ # Rope project settings
112
+ .ropeproject
113
+
114
+ # mkdocs documentation
115
+ /site
116
+
117
+ # mypy
118
+ .mypy_cache/
119
+
120
+ .vscode
121
+ *.swp
122
+
123
+ # osx generated files
124
+ .DS_Store
125
+ .DS_Store?
126
+ .Trashes
127
+ ehthumbs.db
128
+ Thumbs.db
129
+ .idea
130
+
131
+ # pytest
132
+ .pytest_cache
133
+
134
+ # tools/trust-doc-nbs
135
+ docs_src/.last_checked
136
+
137
+ # symlinks to fastai
138
+ docs_src/fastai
139
+ tools/fastai
140
+
141
+ # link checker
142
+ checklink/cookies.txt
143
+
144
+ # .gitconfig is now autogenerated
145
+ .gitconfig
146
+
147
+ _docs
Dockerfile ADDED
@@ -0,0 +1,10 @@
 
 
 
 
 
 
 
 
 
 
 
1
+ FROM python:3.10
2
+ WORKDIR /code
3
+ COPY --link --chown=1000 . .
4
+ RUN mkdir -p /tmp/cache/
5
+ RUN chmod a+rwx -R /tmp/cache/
6
+ ENV HF_HUB_CACHE=HF_HOME
7
+ RUN pip install --no-cache-dir -r requirements.txt
8
+
9
+ ENV PYTHONUNBUFFERED=1 PORT=7860
10
+ CMD ["python", "main.py"]
LICENSE ADDED
@@ -0,0 +1,201 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ Apache License
2
+ Version 2.0, January 2004
3
+ http://www.apache.org/licenses/
4
+
5
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
6
+
7
+ 1. Definitions.
8
+
9
+ "License" shall mean the terms and conditions for use, reproduction,
10
+ and distribution as defined by Sections 1 through 9 of this document.
11
+
12
+ "Licensor" shall mean the copyright owner or entity authorized by
13
+ the copyright owner that is granting the License.
14
+
15
+ "Legal Entity" shall mean the union of the acting entity and all
16
+ other entities that control, are controlled by, or are under common
17
+ control with that entity. For the purposes of this definition,
18
+ "control" means (i) the power, direct or indirect, to cause the
19
+ direction or management of such entity, whether by contract or
20
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
21
+ outstanding shares, or (iii) beneficial ownership of such entity.
22
+
23
+ "You" (or "Your") shall mean an individual or Legal Entity
24
+ exercising permissions granted by this License.
25
+
26
+ "Source" form shall mean the preferred form for making modifications,
27
+ including but not limited to software source code, documentation
28
+ source, and configuration files.
29
+
30
+ "Object" form shall mean any form resulting from mechanical
31
+ transformation or translation of a Source form, including but
32
+ not limited to compiled object code, generated documentation,
33
+ and conversions to other media types.
34
+
35
+ "Work" shall mean the work of authorship, whether in Source or
36
+ Object form, made available under the License, as indicated by a
37
+ copyright notice that is included in or attached to the work
38
+ (an example is provided in the Appendix below).
39
+
40
+ "Derivative Works" shall mean any work, whether in Source or Object
41
+ form, that is based on (or derived from) the Work and for which the
42
+ editorial revisions, annotations, elaborations, or other modifications
43
+ represent, as a whole, an original work of authorship. For the purposes
44
+ of this License, Derivative Works shall not include works that remain
45
+ separable from, or merely link (or bind by name) to the interfaces of,
46
+ the Work and Derivative Works thereof.
47
+
48
+ "Contribution" shall mean any work of authorship, including
49
+ the original version of the Work and any modifications or additions
50
+ to that Work or Derivative Works thereof, that is intentionally
51
+ submitted to Licensor for inclusion in the Work by the copyright owner
52
+ or by an individual or Legal Entity authorized to submit on behalf of
53
+ the copyright owner. For the purposes of this definition, "submitted"
54
+ means any form of electronic, verbal, or written communication sent
55
+ to the Licensor or its representatives, including but not limited to
56
+ communication on electronic mailing lists, source code control systems,
57
+ and issue tracking systems that are managed by, or on behalf of, the
58
+ Licensor for the purpose of discussing and improving the Work, but
59
+ excluding communication that is conspicuously marked or otherwise
60
+ designated in writing by the copyright owner as "Not a Contribution."
61
+
62
+ "Contributor" shall mean Licensor and any individual or Legal Entity
63
+ on behalf of whom a Contribution has been received by Licensor and
64
+ subsequently incorporated within the Work.
65
+
66
+ 2. Grant of Copyright License. Subject to the terms and conditions of
67
+ this License, each Contributor hereby grants to You a perpetual,
68
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
69
+ copyright license to reproduce, prepare Derivative Works of,
70
+ publicly display, publicly perform, sublicense, and distribute the
71
+ Work and such Derivative Works in Source or Object form.
72
+
73
+ 3. Grant of Patent License. Subject to the terms and conditions of
74
+ this License, each Contributor hereby grants to You a perpetual,
75
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
76
+ (except as stated in this section) patent license to make, have made,
77
+ use, offer to sell, sell, import, and otherwise transfer the Work,
78
+ where such license applies only to those patent claims licensable
79
+ by such Contributor that are necessarily infringed by their
80
+ Contribution(s) alone or by combination of their Contribution(s)
81
+ with the Work to which such Contribution(s) was submitted. If You
82
+ institute patent litigation against any entity (including a
83
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
84
+ or a Contribution incorporated within the Work constitutes direct
85
+ or contributory patent infringement, then any patent licenses
86
+ granted to You under this License for that Work shall terminate
87
+ as of the date such litigation is filed.
88
+
89
+ 4. Redistribution. You may reproduce and distribute copies of the
90
+ Work or Derivative Works thereof in any medium, with or without
91
+ modifications, and in Source or Object form, provided that You
92
+ meet the following conditions:
93
+
94
+ (a) You must give any other recipients of the Work or
95
+ Derivative Works a copy of this License; and
96
+
97
+ (b) You must cause any modified files to carry prominent notices
98
+ stating that You changed the files; and
99
+
100
+ (c) You must retain, in the Source form of any Derivative Works
101
+ that You distribute, all copyright, patent, trademark, and
102
+ attribution notices from the Source form of the Work,
103
+ excluding those notices that do not pertain to any part of
104
+ the Derivative Works; and
105
+
106
+ (d) If the Work includes a "NOTICE" text file as part of its
107
+ distribution, then any Derivative Works that You distribute must
108
+ include a readable copy of the attribution notices contained
109
+ within such NOTICE file, excluding those notices that do not
110
+ pertain to any part of the Derivative Works, in at least one
111
+ of the following places: within a NOTICE text file distributed
112
+ as part of the Derivative Works; within the Source form or
113
+ documentation, if provided along with the Derivative Works; or,
114
+ within a display generated by the Derivative Works, if and
115
+ wherever such third-party notices normally appear. The contents
116
+ of the NOTICE file are for informational purposes only and
117
+ do not modify the License. You may add Your own attribution
118
+ notices within Derivative Works that You distribute, alongside
119
+ or as an addendum to the NOTICE text from the Work, provided
120
+ that such additional attribution notices cannot be construed
121
+ as modifying the License.
122
+
123
+ You may add Your own copyright statement to Your modifications and
124
+ may provide additional or different license terms and conditions
125
+ for use, reproduction, or distribution of Your modifications, or
126
+ for any such Derivative Works as a whole, provided Your use,
127
+ reproduction, and distribution of the Work otherwise complies with
128
+ the conditions stated in this License.
129
+
130
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
131
+ any Contribution intentionally submitted for inclusion in the Work
132
+ by You to the Licensor shall be under the terms and conditions of
133
+ this License, without any additional terms or conditions.
134
+ Notwithstanding the above, nothing herein shall supersede or modify
135
+ the terms of any separate license agreement you may have executed
136
+ with Licensor regarding such Contributions.
137
+
138
+ 6. Trademarks. This License does not grant permission to use the trade
139
+ names, trademarks, service marks, or product names of the Licensor,
140
+ except as required for reasonable and customary use in describing the
141
+ origin of the Work and reproducing the content of the NOTICE file.
142
+
143
+ 7. Disclaimer of Warranty. Unless required by applicable law or
144
+ agreed to in writing, Licensor provides the Work (and each
145
+ Contributor provides its Contributions) on an "AS IS" BASIS,
146
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
147
+ implied, including, without limitation, any warranties or conditions
148
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
149
+ PARTICULAR PURPOSE. You are solely responsible for determining the
150
+ appropriateness of using or redistributing the Work and assume any
151
+ risks associated with Your exercise of permissions under this License.
152
+
153
+ 8. Limitation of Liability. In no event and under no legal theory,
154
+ whether in tort (including negligence), contract, or otherwise,
155
+ unless required by applicable law (such as deliberate and grossly
156
+ negligent acts) or agreed to in writing, shall any Contributor be
157
+ liable to You for damages, including any direct, indirect, special,
158
+ incidental, or consequential damages of any character arising as a
159
+ result of this License or out of the use or inability to use the
160
+ Work (including but not limited to damages for loss of goodwill,
161
+ work stoppage, computer failure or malfunction, or any and all
162
+ other commercial damages or losses), even if such Contributor
163
+ has been advised of the possibility of such damages.
164
+
165
+ 9. Accepting Warranty or Additional Liability. While redistributing
166
+ the Work or Derivative Works thereof, You may choose to offer,
167
+ and charge a fee for, acceptance of support, warranty, indemnity,
168
+ or other liability obligations and/or rights consistent with this
169
+ License. However, in accepting such obligations, You may act only
170
+ on Your own behalf and on Your sole responsibility, not on behalf
171
+ of any other Contributor, and only if You agree to indemnify,
172
+ defend, and hold each Contributor harmless for any liability
173
+ incurred by, or claims asserted against, such Contributor by reason
174
+ of your accepting any such warranty or additional liability.
175
+
176
+ END OF TERMS AND CONDITIONS
177
+
178
+ APPENDIX: How to apply the Apache License to your work.
179
+
180
+ To apply the Apache License to your work, attach the following
181
+ boilerplate notice, with the fields enclosed by brackets "[]"
182
+ replaced with your own identifying information. (Don't include
183
+ the brackets!) The text should be enclosed in the appropriate
184
+ comment syntax for the file format. We also recommend that a
185
+ file or class name and description of purpose be included on the
186
+ same "printed page" as the copyright notice for easier
187
+ identification within third-party archives.
188
+
189
+ Copyright [yyyy] [name of copyright owner]
190
+
191
+ Licensed under the Apache License, Version 2.0 (the "License");
192
+ you may not use this file except in compliance with the License.
193
+ You may obtain a copy of the License at
194
+
195
+ http://www.apache.org/licenses/LICENSE-2.0
196
+
197
+ Unless required by applicable law or agreed to in writing, software
198
+ distributed under the License is distributed on an "AS IS" BASIS,
199
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
200
+ See the License for the specific language governing permissions and
201
+ limitations under the License.
README.md CHANGED
@@ -1,10 +1,29 @@
1
- ---
2
- title: Htmltofasthtml
3
- emoji:
4
- colorFrom: purple
5
- colorTo: green
6
- sdk: docker
7
- pinned: false
8
- ---
9
-
10
- Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # FastHTML on 🤗 Spaces
2
+
3
+ Deploy a FastHTML application to [HuggingFace Spaces](https://huggingface.co/spaces) for free with one command!
4
+
5
+ ## Quickstart
6
+
7
+ 1. Create a free account on [HuggingFace](https://huggingface.co)
8
+ 2. Go to your account settings and create an access token with write access. Keep this token safe and don't share it.
9
+ 3. Set the `HF_TOKEN` environment variable to that token
10
+ 4. Install fasthtml-hf: `pip install fasthtml-hf`
11
+ 5. HuggingFace needs `fasthtml-hf` to run your space, so add it to your requirements.txt file.
12
+ 6. At the top of your `main.py` add `from fasthtml_hf import setup_hf_backup`, and just before you run uvicorn add `setup_hf_backup(app)`
13
+ 7. Run `fh_hf_deploy <space_name>`, replacing `<space_name>` with the name you want to give your space.
14
+
15
+ By default this will upload a public space. You can make it private with `--private true`.
16
+
17
+ ## Configuration
18
+
19
+ The space will upload a backup of your database to a [HuggingFace Dataset](https://huggingface.co/datasets). By default it will be private and its name will be `<your-huggingface-id>/space-backup`. You can change this behavior in the `config.ini` file. In not provided, a default file will be created with the contents (note that the `[DEFAULT]` line is required at the top):
20
+
21
+ ```
22
+ [DEFAULT]
23
+ dataset_id = space-backup
24
+ db_dir = data
25
+ private_backup = True
26
+ interval = 15 # number of minutes between periodic backups
27
+ ```
28
+
29
+ If you so choose, you can disable the automatic backups and use [persistent storage](https://huggingface.co/docs/hub/en/spaces-storage#persistent-storage-specs) instead for $5/month (USD).
fasthtml_hf/__init__.py ADDED
@@ -0,0 +1,2 @@
 
 
 
1
+ from .deploy import *
2
+ from .backup import *
fasthtml_hf/backup.py ADDED
@@ -0,0 +1,49 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os, shutil
2
+ os.environ['HF_HUB_DISABLE_PROGRESS_BARS'] = '1'
3
+ import time
4
+ from fastcore.utils import *
5
+ from datetime import datetime
6
+ from huggingface_hub import snapshot_download, upload_folder, create_repo, repo_exists, whoami
7
+
8
+ __all__ = ['download', 'upload', 'setup_hf_backup']
9
+ def _token(): return os.getenv("HF_TOKEN")
10
+
11
+ def get_cfg():
12
+ return Config('.', 'config.ini',
13
+ types=dict(dataset_id=str, db_dir=str, private_backup=bool, interval=int),
14
+ create=dict(dataset_id='space-backup', db_dir='data', private_backup=True, interval=15))
15
+
16
+ def get_dataset_id(cfg):
17
+ did = cfg.dataset_id
18
+ if "/" in did or _token() is None: return did
19
+ return f"{whoami(_token())['name']}/{did}"
20
+
21
+ def download():
22
+ cfg = get_cfg()
23
+ did = get_dataset_id(cfg)
24
+ upload_on_schedule()
25
+ if os.getenv("SPACE_ID") and repo_exists(did, repo_type="dataset", token=_token()):
26
+ cache_path = snapshot_download(repo_id=did, repo_type='dataset', token=_token())
27
+ shutil.copytree(cache_path, cfg.db_dir, dirs_exist_ok=True)
28
+
29
+ def upload():
30
+ cfg = get_cfg()
31
+ if not os.getenv("SPACE_ID"): return
32
+ did = get_dataset_id(cfg)
33
+ create_repo(did, token=_token(), private=True, repo_type='dataset', exist_ok=True)
34
+ upload_folder(folder_path=cfg.db_dir, token=_token(), repo_id=did,
35
+ repo_type='dataset', commit_message=f"backup {datetime.now()}")
36
+
37
+
38
+ @threaded
39
+ def upload_on_schedule():
40
+ cfg = get_cfg()
41
+ while True:
42
+ time.sleep(cfg.interval*60)
43
+ upload()
44
+
45
+
46
+ def setup_hf_backup(app):
47
+ app.on_event("startup")(download)
48
+ app.on_event("shutdown")(upload)
49
+
fasthtml_hf/deploy.py ADDED
@@ -0,0 +1,74 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import datetime
2
+ from huggingface_hub import create_repo, upload_folder, add_space_secret, whoami
3
+ from fastcore.utils import *
4
+ from fastcore.script import *
5
+
6
+ def _mk_docker(python_ver):
7
+ fn = Path('Dockerfile')
8
+ if fn.exists(): return
9
+ packages = Path('packages.txt')
10
+ pkg_line = ''
11
+ reqs = Path('requirements.txt')
12
+ if not reqs.exists(): reqs.write_text('python-fasthtml\nfasthtml-hf\n')
13
+ req_line = f'RUN pip install --no-cache-dir -r requirements.txt'
14
+ if packages.exists():
15
+ pkglist = ' '.join(packages.readlines())
16
+ pkg_line = f'RUN apt-get update -y && apt-get install -y {pkglist}'
17
+
18
+ cts = f"""FROM python:{python_ver}
19
+ WORKDIR /code
20
+ COPY --link --chown=1000 . .
21
+ RUN mkdir -p /tmp/cache/
22
+ RUN chmod a+rwx -R /tmp/cache/
23
+ ENV HF_HUB_CACHE=HF_HOME
24
+ {req_line}
25
+ {pkg_line}
26
+ ENV PYTHONUNBUFFERED=1 PORT=7860
27
+ CMD ["python", "main.py"]
28
+ """
29
+ fn.write_text(cts)
30
+
31
+
32
+ def _mk_README(space_id, termination_grace_period):
33
+ fn = Path('README.md')
34
+ if fn.exists(): return
35
+ cts = f"""
36
+ ---
37
+ title: {space_id}
38
+ emoji: 🚀
39
+ colorFrom: purple
40
+ colorTo: red
41
+ sdk: docker
42
+ app_file: app.py
43
+ pinned: false
44
+ termination_grace_period: {termination_grace_period}
45
+ ---
46
+ """
47
+ fn.write_text(cts)
48
+
49
+ @call_parse
50
+ def deploy(
51
+ space_id:str, # ID of the space to upload to
52
+ token:str=None, # Hugging Face token for authentication
53
+ python_ver:str='3.10', # Version of python to use
54
+ upload:bool_arg=True, # Set to `false` to skip uploading files
55
+ private:bool_arg=False,
56
+ termination_grace_period:str="2m"): # Make the repository private
57
+ "Upload current directory to Hugging Face Spaces"
58
+ if not token: token=os.getenv('HF_TOKEN')
59
+ if not token: return print('No token available')
60
+ if "/" not in space_id: space_id = f"{whoami(token)['name']}/{space_id}"
61
+ _mk_docker(python_ver)
62
+ _mk_README(space_id, termination_grace_period)
63
+ private = bool(private) # `private` can be 0,1 or False. As `create_repo` expects private to be True/False we cast it.
64
+ url = create_repo(space_id, token=token, repo_type='space',
65
+ space_sdk="docker", private=private, exist_ok=True)
66
+ if not upload: return print('Repo created; upload skipped')
67
+ upload_folder(folder_path=Path("."),
68
+ repo_id=space_id, repo_type='space',
69
+ ignore_patterns=['__pycache__/*', '.sesskey', 'deploy_hf.py', 'data/*'],
70
+ commit_message=f"deploy at {datetime.datetime.now()}",
71
+ token=token)
72
+ add_space_secret(space_id, token=token, key="HF_TOKEN", value=token)
73
+ print(f"Deployed space at {url}")
74
+
main.py ADDED
@@ -0,0 +1,18 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from fasthtml.common import *
2
+
3
+ app,rt = fast_app(hdrs=[HighlightJS()])
4
+
5
+ @rt("/convert")
6
+ def post(html:str, attr1st:bool): return Pre(Code(html2ft(html, attr1st=str2bool(attr1st)))) if html else ''
7
+
8
+ @rt("/")
9
+ def get():
10
+ return Titled(
11
+ "Convert HTML to FT",
12
+ Form(hx_post='/convert', target_id="ft", hx_trigger="change from:#attr1st, keyup delay:500ms from:#html")(
13
+ Select(style="width: auto", id="attr1st")(
14
+ Option("Children 1st", value="0", selected=True), Option("Attrs 1st", value="1")),
15
+ Textarea(placeholder='Paste HTML here', id="html", rows=10)),
16
+ Div(id="ft"))
17
+
18
+ serve()
pyproject.toml ADDED
@@ -0,0 +1,28 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ [build-system]
2
+ requires = ["setuptools>=42", "wheel"]
3
+ build-backend = "setuptools.build_meta"
4
+
5
+ [project]
6
+ name = "fasthtml-hf"
7
+ version = "0.1.4"
8
+ description = "Huggingface deployment for FastHTML"
9
+ readme = "README.md"
10
+ license = { file = "LICENSE" }
11
+ authors = [
12
+ { name = "Jeremy Howard", email = "[email protected]" },
13
+ { name = "Freddy Boulton", email = "[email protected]" }
14
+ ]
15
+ requires-python = ">=3.7, <4"
16
+ dependencies = [ "fastcore", "huggingface-hub" ]
17
+ classifiers = [
18
+ "Development Status :: 3 - Alpha",
19
+ "Intended Audience :: Developers",
20
+ "License :: OSI Approved :: Apache Software License"
21
+ ]
22
+
23
+ [project.urls]
24
+ "Homepage" = "https://github.com/AnswerDotAI/fasthtml-hf"
25
+
26
+ [project.entry-points.console_scripts]
27
+ fh_hf_deploy = "fasthtml_hf:deploy"
28
+
requirements.txt ADDED
@@ -0,0 +1,2 @@
 
 
 
1
+ python-fasthtml>=0.0.8
2
+ huggingface-hub>=0.20.0