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