Skip to content

Commit c43a0f2

Browse files
authored
Python script flask sample (#116)
* not working * python script sample not working flask * fixed script working * added runner script * readme * remove folder * revert readme * added gitignore for py files * remove app.py * change script * change readme
1 parent b7cfb66 commit c43a0f2

File tree

4 files changed

+345
-0
lines changed

4 files changed

+345
-0
lines changed

examples/scripts/.gitignore

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
_packagemanagement
2+
*.py
3+
requirements.txt
4+
fable_modules

examples/scripts/README.md

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
# Compile and Run a Python Script
2+
3+
```
4+
dotnet tool restore
5+
```
6+
7+
then run
8+
9+
```
10+
dotnet fsi run_flask_app.fsx flask_sample.fsx
11+
```
12+
13+
this will run using `pip-run` for now requirements.txt manual file is supported but inference via `pipreqs` is not working as expected.
14+
15+
## python dependencies
16+
17+
* python3
18+
* pip
19+
* `pipx` > required, used to install global python packages. DEPENDENCY, [install instructions here](https://pipx.pypa.io/stable/) or for mac via brew.
20+
* `pipreqs` for package requiremnts inference : `pipx install pipreqs` will be executed. (not working yet)
21+
* `pip-run` for running with defualt one shot option, which runs your script one time in a temp env (can be installed with pipx), `pipx install pip-run` will be executed
22+
23+
## pip-run
24+
25+
* [pip-run](https://github.com/jaraco/pip-run/blob/main/README.rst) is used to run all script in "isolation" taking care of creatinv venv and installing libs in temp dirs and removing them after execution

examples/scripts/flask_sample.fsx

Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
#r "nuget: Fable.Core"
2+
#r "nuget: Fable.Python"
3+
#r "nuget: Feliz.ViewEngine"
4+
#r "nuget: Zanaptak.TypedCssClasses"
5+
// #r "nuget: Fli"
6+
open Fable.Python.Builtins
7+
open Feliz.ViewEngine
8+
open Zanaptak.TypedCssClasses
9+
10+
// PIP: flask
11+
open Fable.Python.Flask
12+
13+
type Bulma = CssClasses<"https://cdnjs.cloudflare.com/ajax/libs/bulma/0.9.3/css/bulma.min.css", Naming.PascalCase>
14+
15+
module View =
16+
let title (str: string) = Html.p [ prop.classes [ Bulma.Title ]; prop.text str ]
17+
let subTitle (str: string) = Html.p [ prop.classes [ Bulma.Subtitle ]; prop.text str ]
18+
19+
let model = {|
20+
Title="Fable Python |> F# ♥️ Python"
21+
Description="Demo Website, Fable Python running on Flask!"
22+
Banner="https://unsplash.it/1200/900?random"
23+
PermaLink="https://fable.io"
24+
Author="dag@brattli.net"
25+
Brand="public/favicon.png"
26+
|}
27+
28+
let head =
29+
Html.head [
30+
Html.title [ prop.text model.Title ]
31+
32+
Html.meta [ prop.charset.utf8 ]
33+
Html.meta [ prop.name "author"; prop.content model.Author ]
34+
Html.meta [ prop.name "description"; prop.content model.Description ]
35+
36+
Html.meta [ prop.httpEquiv.contentType; prop.content "text/html"; prop.charset.utf8 ]
37+
Html.meta [ prop.name "viewport"; prop.content "width=device-width, initial-scale=1" ]
38+
39+
Html.meta [
40+
prop.custom ("http-equiv", "Cache-Control")
41+
prop.content "no-cache, no-store, must-revalidate"
42+
]
43+
Html.meta [ prop.custom ("http-equiv", "Pragma"); prop.content "no-cache" ]
44+
Html.meta [ prop.custom ("http-equiv", "Expires"); prop.content "0" ]
45+
46+
Html.link [ prop.rel "icon"; prop.href "public/favicon.ico" ]
47+
Html.link [ prop.rel "stylesheet"; prop.href "https://cdnjs.cloudflare.com/ajax/libs/bulma/0.9.3/css/bulma.min.css"; prop.crossOrigin.anonymous ]
48+
]
49+
50+
let body = Html.div [
51+
title model.Title
52+
subTitle model.Description
53+
]
54+
55+
let section =
56+
Html.section [
57+
prop.classes [ Bulma.Hero; Bulma.IsFullheightWithNavbar ]
58+
prop.style [
59+
style.backgroundImageUrl (model.Banner)
60+
style.backgroundPosition "center"
61+
style.backgroundSize.cover
62+
]
63+
prop.children [
64+
Html.div [
65+
prop.classes [ Bulma.HeroBody; Bulma.IsDark ]
66+
prop.children [ Html.div [ prop.classes [ Bulma.Container ]; prop.children body ] ]
67+
]
68+
]
69+
]
70+
71+
let html =
72+
Html.html [
73+
head
74+
Html.body [
75+
section
76+
]
77+
]
78+
79+
let renderView () =
80+
View.html |> Render.htmlDocument
81+
82+
// NB: this must not be inside a module for Flask to resolve the app correctly!
83+
// https://stackoverflow.com/questions/57718786/error-launching-flask-app-with-error-failed-to-find-flask-application
84+
let app = Flask.Create(__name__, "/public")
85+
86+
// Setup the routes. See if we can use attributes instead
87+
app.route("/")(renderView) |> ignore
88+

examples/scripts/run_flask_app.fsx

Lines changed: 228 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,228 @@
1+
// dotnet fsi run_flask_app flask_sample.fsx [--global](to uses pip-run and venv by default)
2+
#r "nuget: Fli"
3+
#r "nuget: EluciusFTW.SpectreCoff"
4+
//we can use this package to run python commands together with the script
5+
open Fli
6+
open SpectreCoff
7+
8+
"Python + F#" |> figlet |> toConsole
9+
10+
// using vscode F# highlight ext can also execute inline python
11+
let python (p : string)= p
12+
13+
let create_py_venv () =
14+
cli {
15+
Shell Shells.BASH
16+
Command ("python3 -m venv .venv")
17+
}
18+
|> Command.execute
19+
|> Output.printText
20+
21+
let install_pip_run_package() =
22+
"install pip-run" |> C |> toConsole
23+
cli {
24+
Shell Shells.BASH
25+
Command ("pipx install pip-run")
26+
}
27+
|> Command.execute
28+
|> Output.printText
29+
30+
31+
let isGlobal =
32+
match fsi.CommandLineArgs |> Seq.toList with
33+
|_::_::"--global"::[] -> true
34+
|_::"--global"::_ -> true
35+
|_ -> false
36+
37+
let install_pipreqs_package() =
38+
"install pipreqs" |> C |> toConsole
39+
cli {
40+
Shell Shells.BASH
41+
Command ("pipx install pipreqs")
42+
}
43+
|> Command.execute
44+
|> Output.printText
45+
46+
// FAILS...
47+
let gen_pip_requirements_file() =
48+
"execute pipreqs to generate requirements.txt" |> C |> toConsole
49+
cli {
50+
Shell Shells.BASH
51+
Command ("python3 -m pipreqs .")
52+
}
53+
|> Command.execute
54+
|> Output.throwIfErrored
55+
|> Output.printText
56+
57+
let create_requirements() =
58+
cli {
59+
Shell Shells.BASH
60+
Command ("touch requirements.txt")
61+
}
62+
|> Command.execute
63+
|> Output.printText
64+
65+
let gen_pip_requirements_file_local() =
66+
"execute local pipreqs to generate requirements.txt" |> C |> toConsole
67+
cli {
68+
Shell Shells.BASH
69+
Command ("pipreqs .")
70+
}
71+
|> Command.execute
72+
|> Output.throwIfErrored
73+
|> Output.printText
74+
75+
let install_pip_requirements() =
76+
"pip install requirements.txt" |> C |> toConsole
77+
cli {
78+
Shell Shells.BASH
79+
Command ("python3 -m pip install -r requirements.txt ")
80+
}
81+
|> Command.execute
82+
|> Output.throwIfErrored
83+
|> Output.printText
84+
85+
let activate_py_venv () =
86+
cli {
87+
Shell Shells.BASH
88+
Command ("source .venv/bin/activate")
89+
}
90+
|> Command.execute
91+
|> Output.throwIfErrored
92+
|> Output.printText
93+
94+
let which_python () =
95+
cli {
96+
Shell Shells.BASH
97+
Command ("which python")
98+
}
99+
|> Command.execute
100+
|> Output.printText
101+
102+
103+
/// pip install flask
104+
let pip_install_flask() =
105+
cli {
106+
Shell Shells.BASH
107+
Command ("python3 -m pip install flask")
108+
}
109+
|> Command.execute
110+
|> Output.printText
111+
112+
/// pip install extra dependencies if needed, specify them as extra arg ',' separated
113+
let pip_install_extras() =
114+
let dependencies =
115+
match fsi.CommandLineArgs |> Seq.toList with
116+
|_::_::trd::[] -> trd.Split(",")
117+
|_ -> [||]
118+
119+
for dep in dependencies do
120+
cli {
121+
Shell Shells.BASH
122+
Command ($"python3 -m pip install {dep}")
123+
}
124+
|> Command.execute
125+
|> Output.printText
126+
127+
let fable_compile_flask_app() =
128+
"FABLE" |> figlet |> toConsole
129+
130+
let flaskScriptName =
131+
printfn $"args: {fsi.CommandLineArgs}"
132+
match fsi.CommandLineArgs |> Seq.toList with
133+
|_::snd::[] -> snd
134+
|_ -> "app.fsx"
135+
136+
cli {
137+
Shell Shells.BASH
138+
Command ($"dotnet tool restore && dotnet fable {flaskScriptName} --lang Python --noCache")
139+
}
140+
|> Command.execute
141+
|> Output.throwIfErrored
142+
|> Output.printText
143+
144+
let remove_old_app() =
145+
"rm app.py" |> C |> toConsole
146+
cli {
147+
Shell Shells.BASH
148+
Command "rm app.py"
149+
}
150+
|> Command.execute
151+
|> Output.throwIfErrored
152+
|> Output.printText
153+
154+
let rename_and_cleanup() =
155+
"rename latest script to app.py" |> C |> toConsole
156+
cli {
157+
Shell Shells.BASH
158+
Command "mv *.py app.py"
159+
}
160+
|> Command.execute
161+
|> Output.throwIfErrored
162+
|> Output.printText
163+
164+
let deactivate_py_venv() =
165+
cli {
166+
Shell Shells.BASH
167+
Command ("python3 -m deactivate")
168+
}
169+
|> Command.execute
170+
|> Output.printText
171+
172+
let run_flask_app() =
173+
try
174+
"starting app to listen on http://127.0.0.1:5000" |> P |> toConsole
175+
cli {
176+
Shell Shells.BASH
177+
Command ("python3 -m flask run")
178+
}
179+
|> Command.execute
180+
|> Output.throwIfErrored
181+
|> ignore
182+
with ex ->
183+
printfn $"script exited with code {ex.Message}"
184+
()
185+
186+
let one_shot_run_flask_app() =
187+
try
188+
"starting ONE-SHOT app to listen on http://127.0.0.1:5000" |> P |> toConsole
189+
cli {
190+
Shell Shells.BASH
191+
Command ("pip-run flask -r requirements.txt -- -m flask run")
192+
}
193+
|> Command.execute
194+
|> Output.throwIfErrored
195+
|> ignore
196+
with ex ->
197+
$"script exited with code {ex.Message}" |> P |> toConsole
198+
()
199+
200+
201+
let isVenv =
202+
match fsi.CommandLineArgs |> Seq.toList with
203+
|_::_::"--venv"::[] -> true
204+
|_::"--venv"::_ -> true
205+
|_ -> false
206+
207+
// execution
208+
209+
if isVenv then
210+
create_py_venv()
211+
|> activate_py_venv
212+
|> which_python
213+
printfn "remember to run deactivate at the end of the script, or which python"
214+
215+
//install_pipreqs_package()
216+
//|> if isGlobal then gen_pip_requirements_file else gen_pip_requirements_file_local
217+
()
218+
|> create_requirements
219+
|> if isGlobal then id else install_pip_run_package
220+
|> fable_compile_flask_app
221+
|> remove_old_app
222+
|> rename_and_cleanup
223+
|> if isGlobal then run_flask_app else one_shot_run_flask_app
224+
225+
// need a strategy to not break on CTRL-C for this...
226+
// maybe use `fg` ?
227+
// |> deactivate_py_venv
228+
// |> which_python

0 commit comments

Comments
 (0)