Using and Mastering Cookiecutter Transcripts
Chapter: Creating Cookiecutter templates
Lecture: Demo: pre-generation hooks

Login or purchase this course to watch this video and the rest of the course contents.
0:00 Several times you've heard me say things like oh, we could totally validate this, and verify it if we just had this things called hooks.
0:10 So, Cookiecutter itself it's Jinja syntax here and some of its other features, is very flexible and sometimes that's all you need;
0:16 but there is certain things, at least at the time of this recording that you can't do,
0:20 like for example, you can't conditionally include or exclude files you can conditionally include or exclude parts of files
0:28 but the files themselves you can't really do that. So imagine you’re creating a web app, and you ask a question,
0:34 what kind of front-end frameworks do you want to use, do you want t use Foundation, do you want to use Bootstrap,
0:40 there is all these different choices you could make and probably those have a bunch of files, css and json that go with it
0:44 so without this concept of hooks, you can't really do that. So hooks are the way to basically write arbitrary code before and after project creation.
0:52 So let's see how that works, so here I have opened this up into PyCharm, like I said, most of this class and to use Cookiecutter
0:59 you don't need to know Python, you need to have it because it executes on top of the runtime, but other than that, you don't care.
1:05 In this case, you're going to care, you're going to need to be able to write Python because that is how you do it.
1:11 I do suppose there is other options, you can use a shell script, like a bash script, so if you want to write that instead,
1:16 but then that locks you into unix platforms only; and there is some talk that maybe somebody adding a feature
1:23 say for like a PowerShell script for Windows, something like that, but for now,
1:26 the best way to do these hooks is really with Python, so how does that work?
1:29 So we're going to get started by creating a new folder, called hooks, lowercase, just like that, it has to be at the top level of your template,
1:38 not where the files are, or things you're actually using to give to the users are, above that, and here we're going to have
1:44 a Python file and it's going to be called pre_gen_project.py Now, let's go ahead and add another one, we're not going to do anything with this yet,
1:56 but let's go over here and say post_gen_project.py, and let me just do a print "Post gen hook running".
2:02 Alright, so that should show up, don't you think, in our output here, and we'll just do the same for the pre gen hook.
2:09 Just so we can check that everything is setup okay. Alright, so let's go over here to our working directory again, see where we are,
2:16 perfect, so if we look here we have all the stuff we've created, and I've also made a backup of where we were before,
2:22 just like I have a snapshot, so here is our current one, we're going to say cookiecutter to install that.
2:28 And, we need to say local, alright, it's going to say where is my project, I am going to call it the-new-hooked-project, something like that,
2:36 creator we'll leave empty for now, data is now, notice if I hit Enter I just choose the default for the select the favorite color,
2:44 and pre gen hook is running and post gen hook is running. Now, I am not sure if that behaves the way you would expect or not.
2:50 In my mind, the pre gen hook kind of just run before stuff, right, but notice that it's actually asking all of these questions,
2:57 all the prompts are happening first. So what happens, the workflow is basically it gathers,
3:03 it looks to those from the cookiecutter.json, it runs through the whole prompts, it feeds this data here and then in between these two lines
3:09 the actual generation and processing the files happens and then the post gen hooks run.
3:14 Okay, so you already have access to these things which means like creator, we could validate creator, so let's go and work on a validation thing,
3:21 we're going to put post gen hook away for a little while. Okay, we probably don't want to print that out every time that runs,
3:27 that's kind of not great looking, so let's do this, alright, so let's write this function called validate. We're going to come down here
3:31 and this hook file actually gets its values replaced as well, which is pretty interesting, so check this out,
3:39 we'll say the creator is and now what we can put in here is some kind of string like Jeff or Michael or a Jinja expression,
3:46 and the Jinja expression is {{ cookiecutter.creator }} Alright, let's just do a quick print pre hook found creator of
3:56 and do a little format here, okay, so if we run that and then let's just say-
4:02 hold on, there is going to be one small problem, it's not going to do this, why- because we're not running it,
4:10 so we could just come over here and say validate, but let's be a little better so to sense, in Python,
4:14 there is a way to test if this thing is being run or if it's being imported and so there is this little convention here like so.
4:20 So the name of the module is set, the dunder name is set to a __main__.
4:25 It takes a little to get used to if this is new to you, but that will execute this. Okay, so now if I run this, so I'll put something for the creator,
4:33 and we'll just go and choose that and you can see pre gen hook found the creator. Awesome, okay, so check that out, we can just use this
4:41 and what this Python script actually sees, if we run this in some kind of verbose mode and find out where this file gets put,
4:47 on some temporary location, what this sees as it executes as Python it's simply this, like that is what it sees, it's Cookiecutter itself
4:55 that does the transformation of the hook file and then you run it with the values that were dropped in,
5:02 so we write it like this, but effectively imagine the replacements were done before this was ever loaded by Python.
5:08 Alright, so now we can do a little test here, we can say something like this,
5:13 if not creator, so if it's empty or something like that, or not creator.strip(), right,
5:21 I think actually because of the way we'er doing it, it's never going to be none; if it's basically empty, we'll do something like this,
5:30 print("ERROR: You must specify a creator to use this template."). Now, how do we indicate that this was the problem.
5:40 Okay, so what we need to do is we need to say exit and basically the way we tell Cookiecutter there is a problem,
5:46 is we exit with a non zero status code, Python the way you do that
5:50 is you import sys, you say sys.exit(1) let's say 1, right, 1 means there is no creator, we'll add another validation as well.
6:02 Let's try this again, project name will be New project with hook and creator,
6:08 it seems like a great name to me, creator, let's go without the creator first. I don't need that, who cares, boom, error,
6:16 you must specify a creator to use this template, and then cookiecutter says, something with the pre gen hook didn't work
6:24 so we are out of here it exit with code 1, so notice that you can send out different codes, so that when people look at it, it can mean something like
6:32 your documentation for the cookiecutter template can say oh exit code 5 means there was this kind of problem,
6:37 exit code 7 means that kind of problem and so on, okay, so let's run this again, we should be able to do it, the created project.
6:44 And now if I say Michael, or whatever, and we just next through it, boom, done and now we have validation for this thing here which is great.
6:52 So it's not just basic validation, we could do all sorts of things, like we could check to see that the favorite color
7:00 maybe right now we don't support black so we can also come down here
7:03 we could say something to this effect favorite_color = '{{ cookiecutter.favorite_color }}'
7:06 this cookiecutter thing, and we can say if favorite_color == black print("Currently black is actually not implemented.")
7:16 And let's go over here and exit let's see if we say exit 7, that means the color is invalid.
7:24 So let's try it one more time. Black project, we'll never worry about this because it won't get created, j, I'm going to pick black, so we'll say 7,
7:31 boom, currently black is not implemented, error stopping. So we can do all kinds of validation, we could check that
7:39 this entry somehow matches this entry, so you can see these pre generation hooks, lets us look at all the data after it's been entered,
7:46 and verify that everything is correct. If we want to actually mess with the files, we want to make changes to the files themselves,
7:52 well then, that is going to be a post generation hook, which we'll look at in a moment.


Talk Python's Mastodon Michael Kennedy's Mastodon