https://pantsbuild.org/ logo
#general
Title
# general
a

acceptable-night-23367

05/14/2022, 9:30 PM
I'm trying pants for the first time, on a pretty simple flask app (just one
app.py
module, plus some static assets), I've gone through the getting started docs, but can't get it to run the app. It looks like it's starting the initial process correctly, flask (werkzeug) comes up and says it's going to listen on whatever port, and tries to start the child serving process, but that one fails to import flask. It seems like the child process created by werkzeug isn't running with the same environment the parent process is using?
h

happy-kitchen-89482

05/14/2022, 9:38 PM
Hi! Sorry for the trouble. Are you running the flask app with
./pants run
?
a

acceptable-night-23367

05/14/2022, 9:39 PM
yeah
h

happy-kitchen-89482

05/14/2022, 9:39 PM
I recall having this exact issue with the Django dev server, due to how it forks child processes, so I need to remember how I fixed it
a

acceptable-night-23367

05/14/2022, 9:39 PM
BUILD
Copy code
pex_binary(
    name="app",
    entry_point="app.py",
)

python_requirements(
    name="requirements.txt0",
)

python_sources(
    name="root",
)
h

happy-kitchen-89482

05/14/2022, 9:39 PM
Let me go into my mind palace for a sec
šŸ’” 1
a

acceptable-night-23367

05/14/2022, 9:40 PM
haha thanks
h

happy-kitchen-89482

05/14/2022, 9:40 PM
Your cmd line is
./pants run path/to/package:app
?
a

acceptable-night-23367

05/14/2022, 9:40 PM
furrows brow wait it started working
% ./pants run :app
h

happy-kitchen-89482

05/14/2022, 9:41 PM
Ah that BUILD file is in the root
a

acceptable-night-23367

05/14/2022, 9:42 PM
yeah, I don't have a src/python directory, this was previously a really simple thing with just a virtualenv and an app module
h

happy-kitchen-89482

05/14/2022, 9:42 PM
That should be fine
a

acceptable-night-23367

05/14/2022, 9:42 PM
./pants run app.py
also works
h

happy-kitchen-89482

05/14/2022, 9:42 PM
Yep
a

acceptable-night-23367

05/14/2022, 9:42 PM
welp
h

happy-kitchen-89482

05/14/2022, 9:42 PM
OK, so now I'm either confused that it works or confused that it didn't before
a

acceptable-night-23367

05/14/2022, 9:42 PM
same
Copy code
% ./pants run :app
17:35:07.95 [INFO] Completed: Building 2 requirements for app.pex from the 3rdparty/python/default.lock resolve: flask, flask-assets
17:35:08.58 [INFO] Completed: Building local_dists.pex
 * Serving Flask app 'demo' (lazy loading)
 * Environment: production
   WARNING: This is a development server. Do not use it in a production deployment.
   Use a production WSGI server instead.
 * Debug mode: on
 * Running on all addresses (0.0.0.0)
   WARNING: This is a development server. Do not use it in a production deployment.
 * Running on <http://127.0.0.1:5001>
 * Running on <http://192.168.69.100:5001> (Press CTRL+C to quit)
 * Restarting with stat
Traceback (most recent call last):
  File "/usr/lib/python3.10/runpy.py", line 196, in _run_module_as_main
    return _run_code(code, main_globals, None,
  File "/usr/lib/python3.10/runpy.py", line 86, in _run_code
    exec(code, run_globals)
  File "/home/leif/git/demo/app.py", line 8, in <module>
    from flask import Flask, render_template
ModuleNotFoundError: No module named 'flask'
that's what it looked like before
h

happy-kitchen-89482

05/14/2022, 9:45 PM
Right
so... race condition? šŸ¤·ā€ā™‚ļø
a

acceptable-night-23367

05/14/2022, 9:45 PM
not sure
I thought it started working after I activated my old venv but then I deactivated it and it's still working
h

happy-kitchen-89482

05/14/2022, 9:46 PM
Not directly related, but FYI since I'm finding things in the mind palace: You can set
restartable=True
on the
pex_binary
and then the server will reload automatically on edits
šŸ‘šŸ» 1
Including edits to any dependencies: python sources, requirements, resources, etc.
a

acceptable-night-23367

05/14/2022, 9:47 PM
ok well, that was going to be my next question once we solved this: how to manage other resources like static assets, particularly ones that require another tool as a build step (tailwind css, in particular)
h

happy-kitchen-89482

05/14/2022, 9:48 PM
That
Restarting with stat
line looks suspicious, is that what Flask prints when the server restarts due to its own change detection?
Anyway, if the problem rears up again, let us know here!
a

acceptable-night-23367

05/14/2022, 9:49 PM
I think that's it telling me which mechanism it's going to use to watch files for hot reloading
h

happy-kitchen-89482

05/14/2022, 9:49 PM
Got it
a

acceptable-night-23367

05/14/2022, 9:49 PM
the other option would be inotify, I believe
h

happy-kitchen-89482

05/14/2022, 9:50 PM
Got it, so with
restartable=True
Pants will do its own hot reloading, which is more robust than Flask/Django's alone can be, because it knows about all your deps and how to build them from the original sources. E.g., tailwind css in your case
šŸ‘šŸ» 1
a

acceptable-night-23367

05/14/2022, 9:50 PM
I'm guessing it's probably this https://www.pantsbuild.org/docs/plugins-codegen
h

happy-kitchen-89482

05/14/2022, 9:51 PM
Pants supports a lot of code generators out of the box, but tailwind css is not among them
however it shouldn't be too hard to add, and we'd love the contribution if you're up for it! We can guide
āž• 1
Yeah, it's basically codegen
a

acceptable-night-23367

05/14/2022, 9:51 PM
I'll start with that and see how far I get
thanks!
h

happy-kitchen-89482

05/14/2022, 9:51 PM
I'd need to do like 20 minutes of reading about tailwind
h

hundreds-father-404

05/14/2022, 9:53 PM
In the meantime to writing a codegen plugin, another option is to manually generate the files and then load them with a
resources
target
h

happy-kitchen-89482

05/14/2022, 9:53 PM
True, that could be helpful for getting things running initially, but then you don't get the hot reloading
a

acceptable-night-23367

05/14/2022, 9:53 PM
this project is an opportunity for me to learn about pants, so I do want to try to do everything inside the build system
šŸ‘ 1
(like, my goal is to explore pants, not to ship my app; I've already got it shipped as much as I need it)
šŸ‘ 1
h

happy-kitchen-89482

05/14/2022, 9:55 PM
With codgen, generally the things you'd look at are: A) installing the tailwind CLI (I suggest ignoring that at first and just assuming it's available on the PATH, then tackling it later) B) modeling the input targets, C) modeling the output sources, D) Writing the code that turns B) into C)
There are a ton of examples in the repo, e.g., for protobuf and thrift
a

acceptable-night-23367

05/14/2022, 9:56 PM
anyway, about tailwind, I don't understand it super well yet, a friend recommended I try it. It seems like it's just a collection of css classes like
text-white
font-bold
bg-blue-500
so that instead of writing css files, you just do everything in html, and apply the classes you want but there's a build step that takes a bit of tailwind config, plus a simple css template-ish file, and generates a plain css file to bundle in assets
great, thanks šŸ™‚
h

happy-kitchen-89482

05/14/2022, 9:56 PM
And feel free to ask any question on #development at any time!
šŸ™šŸ» 1
h

hundreds-father-404

05/14/2022, 9:56 PM
(I suggest ignoring that at first and just assuming it's available on the PATH, then tackling it later)
Specifically, use an absolute path like
/usr/bin/tailwind
in the
argv=
for a
Process()
h

happy-kitchen-89482

05/14/2022, 9:57 PM
For the record, there are no bad questions, only incomplete documentation...
šŸ’Æ 1
a

acceptable-night-23367

05/14/2022, 9:57 PM
it's a pypi package, so I think that part should be easy
šŸ‘ 1
h

happy-kitchen-89482

05/14/2022, 9:57 PM
Oh I thought it was an NPM thing
if it's PyPI that is definitely easy
a

acceptable-night-23367

05/14/2022, 9:57 PM
er, it is, but there's pytailwindcss which doesn't require npm stuff
h

happy-kitchen-89482

05/14/2022, 9:57 PM
even better
a

acceptable-night-23367

05/14/2022, 9:57 PM
yep!
ok, interesting, I had been using https://webassets.readthedocs.io/en/latest/index.html, which I can configure to run the tailwind generator (and detect when it needs to), but that more happens during app startup, and it expects to be able to write outputs to static/dist/
I think for a truly pantsy build I do want to write a pants plugin so it happens before packing stuff into the pex
āž• 1
it doesn't appear to explain: ā€¢ where many of the names are imported from (e.g. Get, Process, ProcessResult) ā€¢ where the name
request.protocol_target
comes from ā€¢ what digests are at all, or what a snapshot is
h

hundreds-father-404

05/14/2022, 11:06 PM
Agreed. We want to rewrite the plugin docs. It will help to read over the Target API and Rules API chapters first to understand more how plugins work, including what a
Process
is
a

acceptable-night-23367

05/14/2022, 11:06 PM
yeah, I'm working through those now
jumping to "add codegen" first was the wrong move
h

hundreds-father-404

05/14/2022, 11:06 PM
But bump that I generally recommend getting things working first w/ manual generation before doing a plugin -- the learning curve can be high on plugins (which we very much want to improve through better docs + better error messages)
a

acceptable-night-23367

05/14/2022, 11:07 PM
I think I do have that
ok, I've read the rules api stuff, and am pretty confused about the digest api, so I think I'm about ready to give up and go read a book, but I'll try to describe what I think is the biggest challenge here, in case it's helpful product feedback
tailwind has a config file,
tailwind.config.js
, mine looks like this
Copy code
module.exports = {
  content: [
    './templates/**/*.html',
  ],
  theme: {
    colors: {
      running: {
        500: '#9C59D1',
        700: '#A773D1',
      },
      stopped: {
        500: '#FCF434',
        700: '#FCF897',
      },
    },
    extend: {},
  },
  plugins: [],
}
what tailwind does with that
content
array is interesting: it scans all your html to figure out which class names you're actually using, to make sure it only omits the definitions you're going to use, to reduce asset size
which means,
tailwind.config.js
, and all the files matching that glob pattern, need to be inputs to the rule
which means, at best, in the python rule definition you could strip off
module.exports =
, try to parse the rest as json (I guess hope there aren't any comments), and find the other files to include that way; at worst, you might have to evaluate the config with a javascript interpreter to figure it out
the corresponding way to teach flask-assets about tailwind is considerably simpler:
Copy code
class TailwindCSS(ExternalTool):

    name = "tailwindcss"
    options = {
        "tailwindcss": ("binary", "TAILWINDCSS_BIN"),
        "minify": "TAILWINDCSS_MINIFY",
    }

    def input(self, in_, out, source_path, output_path, **kwargs):
        command = self.parse_binary(self.tailwindcss or "tailwindcss")
        command.extend(["--input", "{input}"])
        command.extend(["--output", "{output}"])
        if self.minify:
            command.append("--minify")
        self.subprocess(command, out, in_)

    def output(self, in_, out, **kwargs):
        out.write(in_.read())


register_filter(TailwindCSS)


app = Flask("demo")

assets = Environment(app)
assets.config["tailwindcss_minify"] = True
css = Bundle(
    "src/main.css",
    filters="tailwindcss",
    depends=["tailwind.config.js", "templates/**/*.html"],
    output="dist/main.css",
)
naturally it's not going to be as good as pants at tracking what needs to be rebuilt, but it gets pretty close and is far more accessible as a "plugin" author
not sure if that's helpful or not, but happy to discuss more if y'all want
anyway, happy weekend all, and thanks for the help earlier
h

happy-kitchen-89482

05/15/2022, 3:34 PM
Thanks for the info! Yeah, the plugin API is not as well documented as we'd like and improving that is part of our upcoming overall docs improvement project. Right now one of the more accessible ways of writing a plugin is cargo-culting from existing ones, which is not ideal
But it does seem like with the right documentation and/or prior knowledge, this is pretty amenable to being a plugin. It has the classic shape of a codegen plugin.
For example, that
content
array could be used to do dependency inference
BTW one thing you might be able to do more easily is hack something together with `experimental_shell_command`: https://www.pantsbuild.org/docs/run-shell-commands#the-experimental_shell_command-target