{"id":533,"date":"2017-05-24T23:20:59","date_gmt":"2017-05-25T05:20:59","guid":{"rendered":"http:\/\/lucasmanual.com\/blog\/?p=533"},"modified":"2017-08-19T22:41:45","modified_gmt":"2017-08-20T04:41:45","slug":"cookiecutter-modify-context-in-post_gen_project-py","status":"publish","type":"post","link":"https:\/\/lucasmanual.com\/blog\/cookiecutter-modify-context-in-post_gen_project-py\/","title":{"rendered":"Cookiecutter &#8211; Modify context in post_gen_project.py"},"content":{"rendered":"<p><a href=\"http:\/\/cookiecutter.readthedocs.io\/en\/latest\/readme.html\">Cookiecutter<\/a> is a template where you can setup skeleton of a project, and based on parameters from cookiecutter.json it will prefill all files with the supplied values. <\/p>\n<p><strong>Advance Cookiecutter Question:<\/strong><br \/>\nHow can I add new context based on what was submitted from cookiecutter.json, then come up with my own variations, and pass them back to context\/extra_context to be rendered. <\/p>\n<ul>\n<li>Example1: if {{ cookiecutter.project_name}} == myapp and {{cookiecutter.github_username }} ==lszyba1 then add a new context variable called: mygreatuser=&#8217;ProSupport&#8217;. Then in template files I would use that variable to fill in some values.<\/li>\n<li>Example2: if {{cookiecutter.framework_to_deploy}}==&#8217;pyramid&#8217;:<br \/>\n    deployment_prod_or_dev_file = ask_more_questions(&#8230;.) (This allows me to write my own function to ask more question, if user said pyramid then ask X, if he said django then ask Y.)<\/li>\n<li>Example3:  import os ; workfolder=os.getcwd() ; context[&#8216;workfolder&#8217;]=workfolder (this will insert a new variable I can render in template.\n<\/li>\n<\/ul>\n<p><strong>Solution:<br \/>\n<\/strong><br \/>\nCookiecutter has hooks folder, but this does not allow context to be modified out of the box, so we need to add 10 lines of code.<br \/>\nSince pre_gen_project.py and post_gen_project.py get rendered with only the values from cookiecutter.json, I will use post_get_project.py to do my programming, add more values to context then re-render the files using mako.<br \/>\nThis allows me to have all files rendered with jinja2 syntax (cookiecutter default), and all my template variables will be left alone, and will be rendered by me using mako.<br \/>\nThis also allows me to mix and match code in post_get_project.py where I use cookiecutter original context in my python if statements.<\/p>\n<p>Lets get started:<br \/>\nCreate folder hooks and add post_gen_project.py file<br \/>\n<code><br \/>\nmyapp\/<br \/>\n....<br \/>\n-- {{cookiecutter.folder_name}}<br \/>\n\u00a0\u00a0 |-- {{cookiecutter.package_name}}.conf<br \/>\n   |-- {{cookiecutter.package_name}}.txt<br \/>\n   |-- __init__.py<br \/>\n   |-- README.txt<br \/>\n   |-- cookiecutter.json<br \/>\n-- DESCRIPTION.rst<br \/>\n-- docs<strong><br \/>\n-- hooks<br \/>\n   |-- post_gen_project.py<\/strong><br \/>\n....<br \/>\n<\/code><\/p>\n<p><code>cookiecutter.json contains:<br \/>\n{<br \/>\n\"folder_name\": \"apache2\",<br \/>\n\"package_name\": \"myapp\",<br \/>\n\"domain_name\": \"example.com\",<br \/>\n\"framework_to_deploy\": [\"pyramid\", \"django\"]<br \/>\n}<br \/>\n<\/code><br \/>\nMy .conf file will be called myapp.conf if you just hit enter through prompts.<\/p>\n<hr\/>\n<p>Now lets go into <strong>post_gen_project.py<\/strong><\/p>\n<pre class=\"brush: python; title: ; notranslate\" title=\"\">\r\n#First get cookiecutter context dictionary:\r\ncontext={{cookiecutter}}\r\n#I can add values to it like this.:\r\ncontext['better_package_name']={{cookiecutter.package_name}}+'2'\r\nor\r\ncontext['better_package_name']=context['package_name']+'2'\r\n#Left is new context I will use in mako, the right is context and info supplied from original cookiecutter.json file\r\n\r\n#Add few more:&lt;code&gt;\r\nimport os\r\nworkfolder=os.getcwd()\r\ncontext['workfolder']=os.getcwd()\r\ncontext['user_name']=os.getlogin()\r\n\r\n#Now lets re-render my template(s) with my additional 3 variables (aka workfolder,better_package_name,user_name..)\r\nfrom mako.template import Template\r\nfrom mako.lookup import TemplateLookup\r\n\r\nmylookup = TemplateLookup(directories=[workfolder],strict_undefined=True)\r\n\r\ndef serve_template(templatename, **kwargs):\r\n    mytemplate2 = mylookup.get_template(templatename)\r\n    print('Rendering: ' + templatename)\r\n    return mytemplate2.render(**kwargs)\r\n\r\n#This loops through a files in a workfolder. I need more testing to confirm the work folder is where I think it is, so for now I will name the files explicatively.\r\n#Render each template explicitly\r\ndef save_template(workfolder=None,file_name=None,context=None):\r\n    newtemplate=serve_template(file_name,**context)\r\n    print('Saving: '+workfolder+'\/'+file_name)\r\n    f=open(os.path.join(workfolder,file_name),'w')\r\n    f.write(newtemplate)\r\n    f.close()\r\n\r\n#Now the lets Re-Render my template with my 3 new variables.\r\nfile_name= context['package_name']+'.conf'\r\n#or below is also works.\r\nfile_name= {{cookiecutter.package_name}}+'.conf'\r\nsave_template(workfolder,file_name,context)\r\n<\/pre>\n<p>#Done, now the template contains all my new variables.<\/p>\n<hr\/>\n<p><strong>Here is a template sample.<\/strong><\/p>\n<pre class=\"brush: python; title: ; notranslate\" title=\"\">\r\n##########Start of {{cookiecutter.package_name}}.conf ########\r\n\r\n#I like your new project {{cookiecutter.package_name}}. I think it will be awsome, but you should consider giving it a better name ${better_package_name}. \r\n\r\n#Beginning the configuration per ${user_name} instructions\r\n\r\n#some code,conf,etc...\r\nAlias ${better_package_name}\/{{cookiecutter.package_name}}.txt ${workfolder}\/${package_name}\r\n##########End of {{cookiecutter.package_name}}.conf ########\r\n\r\n<\/pre>\n<hr\/>\n<p><strong>Bonus:<\/strong><\/p>\n<pre class=\"brush: python; title: ; notranslate\" title=\"\">\r\n#Ask questions based on cookiecutter parameters\r\ndef ask_more_questions(question=None):\r\n    try:\r\n        output = input(question)\r\n    except NameError:\r\n        output = raw_input(question)\r\n    return output\r\n\r\n\r\nif context['framework_to_deploy']=='pyramid':\r\n    deployment_prod_or_dev_file=ask_more_questions('What file you want to deploy: [development.ini] or production.ini :')\r\n    context['deployment_prod_or_dev_file']=(deployment_prod_or_dev_file or 'development.ini')\r\n\r\n#Now For every input question from cookiecutter I can do if statements, if a, then b...\r\n\r\n<\/pre>\n<p>Hope you enjoyed it. Have fun creating your awesome new template. Many thanks to cookiecutter team for making simple yet powerful project templating software!<br \/>\nLucasManual.com team<\/p>\n<p>Use the tools available: This post is about being able to programmatically add values based on initial cookiecutter.json context. This is not about which python templating language is better.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Cookiecutter is a template where you can setup skeleton of a project, and based on parameters from cookiecutter.json it will prefill all files with the supplied values. Advance Cookiecutter Question: How can I add new context based on what was submitted from cookiecutter.json, then come up with my own variations, and pass them back to&hellip; <a class=\"more-link\" href=\"https:\/\/lucasmanual.com\/blog\/cookiecutter-modify-context-in-post_gen_project-py\/\">Continue reading <span class=\"screen-reader-text\">Cookiecutter &#8211; Modify context in post_gen_project.py<\/span><\/a><\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[6,3,4,21],"tags":[22,24,15,23,14],"class_list":["post-533","post","type-post","status-publish","format-standard","hentry","category-corporate","category-debian","category-linux","category-python","tag-cookiecutter","tag-django","tag-linux","tag-pyramid","tag-python","entry"],"_links":{"self":[{"href":"https:\/\/lucasmanual.com\/blog\/wp-json\/wp\/v2\/posts\/533","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/lucasmanual.com\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/lucasmanual.com\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/lucasmanual.com\/blog\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/lucasmanual.com\/blog\/wp-json\/wp\/v2\/comments?post=533"}],"version-history":[{"count":24,"href":"https:\/\/lucasmanual.com\/blog\/wp-json\/wp\/v2\/posts\/533\/revisions"}],"predecessor-version":[{"id":558,"href":"https:\/\/lucasmanual.com\/blog\/wp-json\/wp\/v2\/posts\/533\/revisions\/558"}],"wp:attachment":[{"href":"https:\/\/lucasmanual.com\/blog\/wp-json\/wp\/v2\/media?parent=533"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/lucasmanual.com\/blog\/wp-json\/wp\/v2\/categories?post=533"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/lucasmanual.com\/blog\/wp-json\/wp\/v2\/tags?post=533"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}