def main(): parser = argparse.ArgumentParser( description="Statik, the static web site generator for developers." ) parser.add_argument( '-p', '--project', help="The path to your Statik project (default: current directory).", ) parser.add_argument( '-o', '--output', help="The output path into which to place the built project (default: \"public\" directory in input " + "directory).", ) parser.add_argument( '--quickstart', help="Statik will generate a basic directory structure for you in the project directory.", action='store_true', ) parser.add_argument( '-v', '--verbose', help="Whether or not to output verbose logging information (default: false).", action='store_true', ) args = parser.parse_args() project_path = args.project if args.project is not None else os.getcwd() output_path = args.output if args.output is not None else os.path.join(project_path, 'public') configure_logging(verbose=args.verbose) if args.quickstart: generate_quickstart(project_path) else: generate(project_path, output_path=output_path, in_memory=False)
def test_issue(self): test_path = os.path.dirname(os.path.realpath(__file__)) output_data = generate( os.path.join(test_path, 'data-non-root-base'), in_memory=True ) self.assertIn('albums', output_data) self.assertIn('browse', output_data['albums']) self.assertIn('index.html', output_data['albums']['browse']) self.assertIn('test-album1', output_data['albums']) self.assertIn('index.html', output_data['albums']['test-album1']) self.assertIn('test-album2', output_data['albums']) self.assertIn('index.html', output_data['albums']['test-album2']) html = ET.fromstring(output_data['albums']['browse']['index.html']) self.assertEqual('Browse Albums', html.find('head/title').text.strip()) album_els = [el for el in html.findall('body/ul/li/a')] self.assertEqual(2, len(album_els)) self.assertEqual('Test Album 2', album_els[0].text.strip()) self.assertEqual( '/non/standard/albums/test-album2/', album_els[0].attrib['href'] ) self.assertEqual('Test Album 1', album_els[1].text.strip()) self.assertEqual( '/non/standard/albums/test-album1/', album_els[1].attrib['href'] )
def test_issue(self): test_path = os.path.dirname(os.path.realpath(__file__)) output_data = generate( os.path.join(test_path, 'data-non-root-base'), in_memory=True ) self.assertIn('index.html', output_data) self.assertIn('about', output_data) self.assertIn('index.html', output_data['about']) self.assertIn('contact', output_data) self.assertIn('index.html', output_data['contact']) html = ET.fromstring(output_data['index.html']) static_page_links = html.findall("body/div[@class='menu']/ul/li/a") self.assertEqual(2, len(static_page_links)) self.assertEqual('/non/standard/about/', static_page_links[0].attrib['href']) self.assertEqual('/non/standard/contact/', static_page_links[1].attrib['href']) self.assert_static_page_compiles( output_data['about']['index.html'], "About", "Here's the About page." ) self.assert_static_page_compiles( output_data['contact']['index.html'], "Contact", "Here's how to contact us." )
def test_in_memory(self): test_path = os.path.dirname(os.path.realpath(__file__)) output_data = generate(os.path.join(test_path, 'data-themed', 'config-permalinks.yml'), os.path.join(test_path, 'data-themed'), in_memory=True) self.assertIn('index.html', output_data) self.assertIn('test', output_data) self.assertIn('index.html', output_data['test']) tree = ET.fromstring(_str(output_data['test']['index.html'])) heading2 = tree.findall('./body/h2')[0] self.assertEqual('heres-a-heading', heading2.get('id')) permalink2 = tree.findall('./body/h2/a')[0] self.assertEqual('#heres-a-heading', permalink2.get('href')) self.assertEqual('permalink', permalink2.get('class')) self.assertEqual('Permalink to this heading', permalink2.get('title')) heading3 = tree.findall('./body/h3')[0] self.assertEqual('heres-another-sub-heading', heading3.get('id')) permalink3 = tree.findall('./body/h3/a')[0] self.assertEqual('#heres-another-sub-heading', permalink3.get('href')) self.assertEqual('permalink', permalink3.get('class')) self.assertEqual('Permalink to this heading', permalink3.get('title'))
def test_in_memory(self): test_path = os.path.dirname(os.path.realpath(__file__)) output_data = generate(os.path.join(test_path, 'data-themed', 'config-custom-md-exts.yml'), os.path.join(test_path, 'data-themed'), in_memory=True) self.assertIn('index.html', output_data) self.assertIn('test', output_data) self.assertIn('test-with-code', output_data) self.assertIn('index.html', output_data['test']) self.assertIn('index.html', output_data['test-with-code']) tree = ET.fromstring(output_data['test-with-code']['index.html']) self.assertEqual("Test post with some code", tree.findall('./head/title')[0].text.strip()) self.assertEqual("This should test the codehilite integration.", tree.findall('./body/p')[0].text.strip()) self.assertEqual("codehilite", tree.findall('./body/pre')[0].get('class')) self.assertEqual("language-python", tree.findall('./body/pre/code')[0].get('class')) self.assertEqual( "And now some more code, but in a different language:", tree.findall('./body/p')[1].text.strip()) self.assertEqual("codehilite", tree.findall('./body/pre')[1].get('class')) self.assertEqual("language-c", tree.findall('./body/pre/code')[1].get('class'))
def test_mustache_templating(self): test_path = os.path.dirname(os.path.realpath(__file__)) # Run the Statik generator on our unit test data project, put the # result in memory output_data = generate( os.path.join(test_path, 'data-mustache'), in_memory=True ) self.assert_homepage_compiles(output_data) self.assert_posts_compile(output_data)
def test_self_referencing_models(self): test_path = os.path.dirname(os.path.realpath(__file__)) output_data = generate(os.path.join(test_path, "data-self-referencing"), in_memory=True) self.assertIn('index.html', output_data) homepage = ET.fromstring(output_data['index.html']) parents = homepage.findall('body/ul/li') self.assertEqual('Parent 1', parents[0].text.strip()) children = set([c.text.strip() for c in parents[0].findall('ul/li')]) self.assertEqual({'Child 1.1', 'Child 1.2'}, children) self.assertEqual('Parent 2', parents[1].text.strip()) children = set([c.text.strip() for c in parents[1].findall('ul/li')]) self.assertEqual({'Child 2.1', 'Child 2.2'}, children)
def test_self_referencing_models(self): test_path = os.path.dirname(os.path.realpath(__file__)) output_data = generate( os.path.join(test_path, "data-self-referencing"), in_memory=True ) self.assertIn('index.html', output_data) homepage = ET.fromstring(output_data['index.html']) parents = homepage.findall('body/ul/li') self.assertEqual('Parent 1', parents[0].text.strip()) children = set([c.text.strip() for c in parents[0].findall('ul/li')]) self.assertEqual({'Child 1.1', 'Child 1.2'}, children) self.assertEqual('Parent 2', parents[1].text.strip()) children = set([c.text.strip() for c in parents[1].findall('ul/li')]) self.assertEqual({'Child 2.1', 'Child 2.2'}, children)
def test_theme2(self): test_path = os.path.dirname(os.path.realpath(__file__)) output_data = generate( os.path.join(test_path, 'data-themed', 'config-theme2.yml'), os.path.join(test_path, 'data-themed'), in_memory=True, ) self.assertIn('index.html', output_data) self.assertIn('override-me', output_data) self.assertIn('index.html', output_data['override-me']) # parse the home page homepage = ET.fromstring(output_data['index.html']) self.assertEqual('Home - Theme 2', homepage.findall('./head/title')[0].text.strip()) self.assertEqual('/assets/theme2.css', homepage.findall('./head/link')[0].attrib['href']) self.check_override_page(output_data['override-me']['index.html'])
def test_in_memory(self): test_path = os.path.dirname(os.path.realpath(__file__)) output_data = generate( os.path.join(test_path, 'data-themed', 'config-custom-md-exts.yml'), os.path.join(test_path, 'data-themed'), in_memory=True ) self.assertIn('index.html', output_data) self.assertIn('test', output_data) self.assertIn('test-with-code', output_data) self.assertIn('index.html', output_data['test']) self.assertIn('index.html', output_data['test-with-code']) tree = ET.fromstring(_str(output_data['test-with-code']['index.html'])) self.assertEqual("Test post with some code", tree.findall('./head/title')[0].text.strip()) self.assertEqual( "This should test the codehilite integration.", tree.findall('./body/p')[0].text.strip() ) self.assertEqual( "codehilite", tree.findall('./body/pre')[0].get('class') ) self.assertEqual( "language-python", tree.findall('./body/pre/code')[0].get('class') ) self.assertEqual( "And now some more code, but in a different language:", tree.findall('./body/p')[1].text.strip() ) self.assertEqual( "codehilite", tree.findall('./body/pre')[1].get('class') ) self.assertEqual( "language-c", tree.findall('./body/pre/code')[1].get('class') )
def test_issue(self): test_path = os.path.dirname(os.path.realpath(__file__)) output_data = generate(os.path.join(test_path, 'data-non-root-base'), in_memory=True) self.assertIn('albums', output_data) self.assertIn('browse', output_data['albums']) self.assertIn('index.html', output_data['albums']['browse']) self.assertIn('test-album1', output_data['albums']) self.assertIn('index.html', output_data['albums']['test-album1']) self.assertIn('test-album2', output_data['albums']) self.assertIn('index.html', output_data['albums']['test-album2']) html = ET.fromstring(output_data['albums']['browse']['index.html']) self.assertEqual('Browse Albums', html.find('head/title').text.strip()) album_els = [el for el in html.findall('body/ul/li/a')] self.assertEqual(2, len(album_els)) self.assertEqual('Test Album 2', album_els[0].text.strip()) self.assertEqual('/non/standard/albums/test-album2/', album_els[0].attrib['href']) self.assertEqual('Test Album 1', album_els[1].text.strip()) self.assertEqual('/non/standard/albums/test-album1/', album_els[1].attrib['href'])
def test_issue(self): test_path = os.path.dirname(os.path.realpath(__file__)) output_data = generate(os.path.join(test_path, 'data-non-root-base'), in_memory=True) self.assertIn('index.html', output_data) self.assertIn('about', output_data) self.assertIn('index.html', output_data['about']) self.assertIn('contact', output_data) self.assertIn('index.html', output_data['contact']) html = ET.fromstring(output_data['index.html']) static_page_links = html.findall("body/div[@class='menu']/ul/li/a") self.assertEqual(2, len(static_page_links)) self.assertEqual('/non/standard/about/', static_page_links[0].attrib['href']) self.assertEqual('/non/standard/contact/', static_page_links[1].attrib['href']) self.assert_static_page_compiles(output_data['about']['index.html'], "About", "Here's the About page.") self.assert_static_page_compiles(output_data['contact']['index.html'], "Contact", "Here's how to contact us.")
def main(): parser = argparse.ArgumentParser( description="Statik: a static web site generator for developers.") parser.add_argument( '--version', help='Display version info for Statik', action='store_true', ) parser.add_argument( '--quickstart', help= "Statik will generate a basic directory structure for you in the project directory and exit.", action='store_true', ) parser.add_argument( '-p', '--project', help= "The path to your Statik project or project YAML configuration file (default: current directory).", ) parser.add_argument( '-o', '--output', help= "The output path into which to place the built project (default: \"public\" directory in input " + "directory).", ) parser.add_argument( '-w', '--watch', help= "Statik will watch the project path for changes and automatically regenerate the project. " + "This also runs a small HTTP server to serve your output files.", action='store_true', ) parser.add_argument( '--host', help= "When watching a folder for changes (--watch), this specifies the host IP address or hostname to which " + "to bind (default: localhost).", default='localhost', ) parser.add_argument( '--port', help= "When watching a folder for changes (--watch), this specifies the port to which to bind (default: 8000).", type=int, default=8000, ) parser.add_argument( '-n', '--no-browser', action='store_true', default=False, help= "Do not attempt to automatically open a web browser at the served URL when watching for changes" ) parser.add_argument( '-s', '--safe-mode', action='store_true', default=False, help="Run Statik in safe mode (which disallows unsafe query execution)" ) parser.add_argument( '-v', '--verbose', help= "Whether or not to output verbose logging information (default: false).", action='store_true', ) args = parser.parse_args() try: _project_path = args.project if args.project is not None else os.getcwd( ) project_path, config_file_path = get_project_config_file( _project_path, StatikProject.CONFIG_FILE) output_path = args.output if args.output is not None else os.path.join( project_path, 'public') configure_logging(verbose=args.verbose) if args.version: from statik import __version__ logger.info('Statik v%s' % __version__) elif args.watch: watch(config_file_path, output_path, host=args.host, port=args.port, open_browser=(not args.no_browser), safe_mode=args.safe_mode) elif args.quickstart: generate_quickstart(project_path) else: generate(config_file_path, output_path=output_path, in_memory=False, safe_mode=args.safe_mode) except StatikError as e: logger.exception( "Exception caught while attempting to generate project", e) sys.exit(e.exit_code) except Exception as e: logger.exception( "Exception caught while attempting to generate project", e) sys.exit(1) # success sys.exit(0)
def test_in_memory(self): test_path = os.path.dirname(os.path.realpath(__file__)) # Run the Statik generator on our unit test data project, put the # result in memory output_data = generate( os.path.join(test_path, 'data-simple'), in_memory=True ) # Check that the home page is there self.assertIn('index.html', output_data) # Check that the generated posts are there self.assert_path_exists("2016/06/12/andrew-hello-world/index.html", output_data) self.assert_path_exists("2016/06/18/second-post/index.html", output_data) self.assert_path_exists("2016/06/25/andrew-second-post/index.html", output_data) self.assert_path_exists("2016/06/30/tables-test/index.html", output_data) self.assert_path_exists("tag-testing/index.html", output_data) self.assert_path_exists("overlap/index.html", output_data) self.assert_path_exists("overlap/andrew-hello-world/index.html", output_data) self.assert_path_exists("overlap/my-first-post/index.html", output_data) self.assert_path_exists("overlap/second-post/index.html", output_data) self.assert_path_exists("overlap/andrew-second-post/index.html", output_data) # Check that the paged posts exist self.assert_path_exists("paged-posts/1/index.html", output_data) self.assert_path_exists("paged-posts/2/index.html", output_data) self.assert_path_exists("paged-posts/3/index.html", output_data) # Check that the homepage compiles properly self.assert_homepage_compiles(output_data['index.html']) # Check that the post "my-first-post" compiles properly self.assert_my_first_post_compiles(self.assert_path_exists("2016/06/15/my-first-post/index.html", output_data)) # Check that the two bios compiled properly self.assert_michael_bio_compiles(self.assert_path_exists("bios/michael/index.html", output_data)) self.assert_andrew_bio_compiles(self.assert_path_exists("bios/andrew/index.html", output_data)) # Test the for-each context rendering self.assert_by_author_andrew_compiles(self.assert_path_exists("by-author/andrew/index.html", output_data)) self.assert_by_author_michael_compiles(self.assert_path_exists("by-author/michael/index.html", output_data)) # Test the custom template tags/filters functionality tt = ET.fromstring(output_data['tag-testing']['index.html']) self.assertEqual('html', tt.findall('.')[0].tag) para_tags = tt.findall('./body/p') self.assertEqual('Hello world!', para_tags[0].text.strip()) self.assertEqual('an uppercase string', para_tags[1].text.strip()) # Check the contents of the overlapping simple/complex views ov = ET.fromstring(output_data['overlap']['index.html']) self.assertEqual('html', ov.findall('.')[0].tag) self.assertEqual('Overlap Test', ov.findall('./head/title')[0].text.strip()) self.assertEqual('Overlap Test', ov.findall('./body/h1')[0].text.strip()) ov = ET.fromstring(output_data['overlap']['andrew-hello-world']['index.html']) self.assertEqual('html', ov.findall('.')[0].tag) self.assertEqual('Overlap Test', ov.findall('./head/title')[0].text.strip()) self.assertEqual('Andrew says Hello World', ov.findall('./body/h1')[0].text.strip()) ov = ET.fromstring(output_data['overlap']['my-first-post']['index.html']) self.assertEqual('html', ov.findall('.')[0].tag) self.assertEqual('Overlap Test', ov.findall('./head/title')[0].text.strip()) self.assertEqual('My first post', ov.findall('./body/h1')[0].text.strip()) ov = ET.fromstring(output_data['overlap']['second-post']['index.html']) self.assertEqual('html', ov.findall('.')[0].tag) self.assertEqual('Overlap Test', ov.findall('./head/title')[0].text.strip()) self.assertEqual('Second post', ov.findall('./body/h1')[0].text.strip()) ov = ET.fromstring(output_data['overlap']['andrew-second-post']['index.html']) self.assertEqual('html', ov.findall('.')[0].tag) self.assertEqual('Overlap Test', ov.findall('./head/title')[0].text.strip()) self.assertEqual("Andrew's Second Post", ov.findall('./body/h1')[0].text.strip()) # Test the Markdown table generation tables = ET.fromstring(output_data['2016']['06']['30']['tables-test']['index.html']) self.assertEqual('html', tables.findall('.')[0].tag) headings = tables.findall("./body/div[@class='content']/table/thead/tr/th") self.assertEqual(3, len(headings)) self.assertEqual(['Heading 1', 'Heading 2', 'Heading 3'], [el.text.strip() for el in headings]) cells = tables.findall("./body/div[@class='content']/table/tbody/tr/td") self.assertEqual(6, len(cells)) self.assertEqual( ['One', 'Two', 'Three', 'Four', 'Five', 'Six'], [el.text.strip() for el in cells] ) # Now test for the pagination pp = ET.fromstring(output_data['paged-posts']['1']['index.html']) self.assertEqual('html', pp.findall('.')[0].tag) self.assertEqual('Page 1 of 3', pp.findall('./head/title')[0].text.strip()) self.assertEqual('Page 1 of 3', pp.findall('./body/h1')[0].text.strip()) pp_els = pp.findall('./body/ul/li/a') pp_links = [el.attrib['href'] for el in pp_els] pp_link_titles = [el.text.strip() for el in pp_els] self.assertEqual( [ '/2016/06/30/tables-test/', '/2016/06/25/andrew-second-post/' ], pp_links, ) self.assertEqual( [ 'Testing Markdown tables', 'Andrew\'s Second Post' ], pp_link_titles, ) pp = ET.fromstring(output_data['paged-posts']['2']['index.html']) self.assertEqual('html', pp.findall('.')[0].tag) self.assertEqual('Page 2 of 3', pp.findall('./head/title')[0].text.strip()) self.assertEqual('Page 2 of 3', pp.findall('./body/h1')[0].text.strip()) pp_els = pp.findall('./body/ul/li/a') pp_links = [el.attrib['href'] for el in pp_els] pp_link_titles = [el.text.strip() for el in pp_els] self.assertEqual( [ '/2016/06/18/second-post/', '/2016/06/15/my-first-post/' ], pp_links, ) self.assertEqual( [ 'Second post', 'My first post' ], pp_link_titles, ) pp = ET.fromstring(output_data['paged-posts']['3']['index.html']) self.assertEqual('html', pp.findall('.')[0].tag) self.assertEqual('Page 3 of 3', pp.findall('./head/title')[0].text.strip()) self.assertEqual('Page 3 of 3', pp.findall('./body/h1')[0].text.strip()) pp_els = pp.findall('./body/ul/li/a') pp_links = [el.attrib['href'] for el in pp_els] pp_link_titles = [el.text.strip() for el in pp_els] self.assertEqual( [ '/2016/06/12/andrew-hello-world/' ], pp_links, ) self.assertEqual( [ 'Andrew says Hello World' ], pp_link_titles, ) self.assert_mlalchemy_complex_path_view_compiles(output_data) self.assert_homepage_compiles(self.assert_path_exists("mlalchemy/posts/index.html", output_data))
def main(): parser = argparse.ArgumentParser( description="Statik: a static web site generator for developers." ) parser.add_argument( '--version', help='Display version info for Statik', action='store_true', ) parser.add_argument( '--quickstart', help="Statik will generate a basic directory structure for you in the project directory and exit.", action='store_true', ) parser.add_argument( '-p', '--project', help="The path to your Statik project or project YAML configuration file (default: current directory).", ) parser.add_argument( '-o', '--output', help="The output path into which to place the built project (default: \"public\" directory in input " + "directory).", ) parser.add_argument( '-w', '--watch', help="Statik will watch the project path for changes and automatically regenerate the project. " + "This also runs a small HTTP server to serve your output files.", action='store_true', ) parser.add_argument( '--host', help="When watching a folder for changes (--watch), this specifies the host IP address or hostname to which " + "to bind (default: localhost).", default='localhost', ) parser.add_argument( '--port', help="When watching a folder for changes (--watch), this specifies the port to which to bind (default: 8000).", type=int, default=8000, ) parser.add_argument( '-n', '--no-browser', action='store_true', default=False, help="Do not attempt to automatically open a web browser at the served URL when watching for changes" ) parser.add_argument( '-s', '--safe-mode', action='store_true', default=False, help="Run Statik in safe mode (which disallows unsafe query execution)" ) parser.add_argument( '-v', '--verbose', help="Whether or not to output verbose logging information (default: false).", action='store_true', ) args = parser.parse_args() try: _project_path = args.project if args.project is not None else os.getcwd() project_path, config_file_path = get_project_config_file(_project_path, StatikProject.CONFIG_FILE) output_path = args.output if args.output is not None else os.path.join(project_path, 'public') configure_logging(verbose=args.verbose) if args.version: from statik import __version__ logger.info('Statik v%s' % __version__) elif args.watch: watch(config_file_path, output_path, host=args.host, port=args.port, open_browser=(not args.no_browser), safe_mode=args.safe_mode) elif args.quickstart: generate_quickstart(project_path) else: generate(config_file_path, output_path=output_path, in_memory=False, safe_mode=args.safe_mode) except StatikError as e: logger.exception("Exception caught while attempting to generate project", e) sys.exit(e.exit_code) except Exception as e: logger.exception("Exception caught while attempting to generate project", e) sys.exit(1) # success sys.exit(0)
def main(): parser = argparse.ArgumentParser( description="Statik: a static web site generator for developers." ) parser.add_argument( '-p', '--project', help="The path to your Statik project or project YAML configuration file (default: current directory).", ) parser.add_argument( '--quickstart', help="Statik will generate a basic directory structure for you in the project directory and exit.", action='store_true', ) parser.add_argument( '--autogen', help="Statik will generate default views and templates in the project directory for all the models.", action='store_true', ) group_generate = parser.add_argument_group('static site generation') group_generate.add_argument( '-o', '--output', help="The output path into which to place the built project (default: \"public\" directory in input " + "directory).", ) group_generate.add_argument( '--clear-output', action='store_true', help="Clears the output folder first prior to building the project. If watching " + "is initiated, this will only clear the output folder once-off." ) group_generate.add_argument( '-s', '--safe-mode', action='store_true', default=False, help="Run Statik in safe mode (which disallows unsafe query execution)" ) group_server = parser.add_argument_group('built-in server') group_server.add_argument( '-w', '--watch', help="Statik will watch the project path for changes and automatically regenerate the project. " + "This also runs a small HTTP server to serve your output files.", action='store_true', ) group_server.add_argument( '--host', help="When watching a folder for changes (--watch), this specifies the host IP address or hostname to which " + "to bind (default: localhost).", default='localhost', ) group_server.add_argument( '--port', help="When watching a folder for changes (--watch), this specifies the port to which to bind (default: 8000).", type=int, default=8000, ) group_server.add_argument( '-n', '--no-browser', action='store_true', default=False, help="Do not attempt to automatically open a web browser at the served URL when watching for changes" ) group_remote = parser.add_argument_group('remote publishing') group_remote.add_argument( '-u', '--upload', action='store', help="Upload project to remote location (supported: SFTP, netlify)", ) group_remote.add_argument( '--netlify-site-id', action='store', help="Netlify site id to upload to. (--upload=netlify must be specified too)", ) group_remote.add_argument( '-c', '--clear-remote', action='store_true', help="CAUTION: This will delete ALL files and subfolders in the remote folder before uploading the generated files. " + "If the subsequent upload fails, your website will no longer be online." ) group_info = parser.add_argument_group('information about Statik') group_info.add_argument( '-v', '--verbose', help="Whether or not to output verbose logging information (default: false).", action='store_true', ) group_info.add_argument( '--quiet', default=False, help="Run Statik in quiet mode, where there will be no output except upon error.", action='store_true' ) group_info.add_argument( '--fail-silently', default=False, help="Only relevant if running Statik in quiet mode - if Statik encounters an error, the only indication " "of this will be in the resulting error code returned by the process. No other output will be given " "on the terminal.", action='store_true' ) group_info.add_argument( '--no-colorlog', action='store_true', help="By default, Statik outputs logging data in colour. By specifying this switch, " + "coloured logging will be turned off." ) group_info.add_argument( '--version', help='Display version info for Statik', action='store_true', ) args = parser.parse_args() error_context = StatikErrorContext() try: _project_path = args.project if args.project is not None else os.getcwd() project_path, config_file_path = get_project_config_file( _project_path, StatikProject.CONFIG_FILE ) output_path = args.output if args.output is not None else \ os.path.join(project_path, 'public') configure_logging( verbose=args.verbose, quiet=args.quiet, fail_silently=args.fail_silently, colourise=not args.no_colorlog ) if args.fail_silently and not args.quiet: logger.warning("Ignoring --fail-silently switch because --quiet is not specified") if args.version: show_version() sys.exit(0) if args.clear_output: shutil.rmtree(output_path, ignore_errors=True) logger.info("Cleared output path: %s", output_path) if args.watch: watch( config_file_path, output_path, host=args.host, port=args.port, open_browser=(not args.no_browser), safe_mode=args.safe_mode, error_context=error_context ) elif args.quickstart: generate_quickstart(project_path) elif args.autogen: autogen(project_path) else: if args.host and '--host=localhost' in sys.argv[1:]: logger.warning("Ignoring --host argument because --watch is not specified") if args.port and '--port=8000' in sys.argv[1:]: logger.warning("Ignoring --port argument because --watch is not specified") if args.no_browser: logger.warning("Ignoring --no-browser argument because --watch is not specified") generate( config_file_path, output_path=output_path, in_memory=False, safe_mode=args.safe_mode, error_context=error_context ) if args.upload and args.upload == 'SFTP': upload_sftp( config_file_path, output_path, rm_remote=args.clear_remote ) elif args.netlify_site_id and args.upload == 'netlify': if args.clear_remote: logger.warning("--clear-remote is not supported when uploading to Netlify") upload_netlify( output_path, args.netlify_site_id ) else: if args.clear_remote: logger.warning("Ignoring --clear-remote switch because --upload is not specified") if args.netlify_site_id and args.upload != 'netlify' or args.netlify_site_id: logger.warning("Ignoring --netlify-site-id: --upload=netlify not specified") if args.upload: logger.warning("Upload format '{}' not supported".format(args.upload)) except StatikError as e: sys.exit(e.exit_code) except Exception as e: logger.exception("Exception caught while attempting to generate project") sys.exit(1) # success sys.exit(0)
def main(): parser = argparse.ArgumentParser( description="Statik, the static web site generator for developers." ) parser.add_argument( '-p', '--project', help="The path to your Statik project or project YAML configuration file (default: current directory).", ) parser.add_argument( '-o', '--output', help="The output path into which to place the built project (default: \"public\" directory in input " + "directory).", ) parser.add_argument( '-w', '--watch', help="Statik will watch the project path for changes and automatically regenerate the project. " + "This also runs a small HTTP server to serve your output files.", action='store_true', ) parser.add_argument( '--host', help="When watching a folder for changes (--watch), this specifies the host IP address or hostname to which " + "to bind (default: localhost).", default='localhost', ) parser.add_argument( '--port', help="When watching a folder for changes (--watch), this specifies the port to which to bind (default: 8000).", type=int, default=8000, ) parser.add_argument( '--quickstart', help="Statik will generate a basic directory structure for you in the project directory.", action='store_true', ) parser.add_argument( '-v', '--verbose', help="Whether or not to output verbose logging information (default: false).", action='store_true', ) parser.add_argument( '--version', help='Display version info for Statik', action='store_true', ) args = parser.parse_args() _project_path = args.project if args.project is not None else os.getcwd() project_path, config_file_path = get_project_config_file(_project_path, StatikProject.CONFIG_FILE) output_path = args.output if args.output is not None else os.path.join(project_path, 'public') configure_logging(verbose=args.verbose) if args.version: from statik import __version__ logger.info('Statik v%s' % __version__) elif args.watch: watch(config_file_path, output_path, host=args.host, port=args.port) elif args.quickstart: generate_quickstart(project_path) else: generate(config_file_path, output_path=output_path, in_memory=False)
def generate(self): generate(self.project_path, self.output_path, in_memory=False)
def test_in_memory(self): test_path = os.path.dirname(os.path.realpath(__file__)) # Run the Statik generator on our unit test data project, put the # result in memory output_data = generate(os.path.join(test_path, 'data-simple'), in_memory=True) # Check that the home page is there self.assertIn('index.html', output_data) # Check that the generated .htaccess file is in the root of the output data self.assertIn('.htaccess', output_data) self.assertEqual(EXPECTED_HTACCESS_CONTENT, output_data['.htaccess'].strip()) # Check that the generated posts are there self.assert_path_exists( "2018/02/12/unicode-in-markdown-test/index.html", output_data) self.assert_path_exists("2018/01/20/test-datetime-format/index.html", output_data) self.assert_path_exists("2016/06/12/andrew-hello-world/index.html", output_data) self.assert_path_exists("2016/06/18/second-post/index.html", output_data) self.assert_path_exists("2016/06/25/andrew-second-post/index.html", output_data) self.assert_path_exists("2016/06/30/tables-test/index.html", output_data) self.assert_path_exists("tag-testing/index.html", output_data) self.assert_path_exists("overlap/index.html", output_data) self.assert_path_exists("overlap/andrew-hello-world/index.html", output_data) self.assert_path_exists("overlap/my-first-post/index.html", output_data) self.assert_path_exists("overlap/second-post/index.html", output_data) self.assert_path_exists("overlap/andrew-second-post/index.html", output_data) # Check that the paged posts exist self.assert_path_exists("paged-posts/1/index.html", output_data) self.assert_path_exists("paged-posts/2/index.html", output_data) self.assert_path_exists("paged-posts/3/index.html", output_data) self.assert_path_exists("paged-posts/4/index.html", output_data) # Check that the Unicode support for Markdown works as intended self.assert_unicode_content_compiles(output_data) # Check that the homepage compiles properly self.assert_homepage_compiles(output_data['index.html']) # Check that the post "my-first-post" compiles properly self.assert_my_first_post_compiles( self.assert_path_exists("2016/06/15/my-first-post/index.html", output_data)) # Check that the two bios compiled properly self.assert_michael_bio_compiles( self.assert_path_exists("bios/michael/index.html", output_data)) self.assert_andrew_bio_compiles( self.assert_path_exists("bios/andrew/index.html", output_data)) # Test the for-each context rendering self.assert_by_author_andrew_compiles( self.assert_path_exists("by-author/andrew/index.html", output_data)) self.assert_by_author_michael_compiles( self.assert_path_exists("by-author/michael/index.html", output_data)) # Test the custom template tags/filters functionality tt = ET.fromstring(output_data['tag-testing']['index.html']) self.assertEqual('html', tt.findall('.')[0].tag) para_tags = tt.findall('./body/p') self.assertEqual('Hello world!', para_tags[0].text.strip()) self.assertEqual('an uppercase string', para_tags[1].text.strip()) # Check the contents of the overlapping simple/complex views ov = ET.fromstring(output_data['overlap']['index.html']) self.assertEqual('html', ov.findall('.')[0].tag) self.assertEqual('Overlap Test', ov.findall('./head/title')[0].text.strip()) self.assertEqual('Overlap Test', ov.findall('./body/h1')[0].text.strip()) ov = ET.fromstring( output_data['overlap']['andrew-hello-world']['index.html']) self.assertEqual('html', ov.findall('.')[0].tag) self.assertEqual('Overlap Test', ov.findall('./head/title')[0].text.strip()) self.assertEqual('Andrew says Hello World', ov.findall('./body/h1')[0].text.strip()) ov = ET.fromstring( output_data['overlap']['my-first-post']['index.html']) self.assertEqual('html', ov.findall('.')[0].tag) self.assertEqual('Overlap Test', ov.findall('./head/title')[0].text.strip()) self.assertEqual('My first post', ov.findall('./body/h1')[0].text.strip()) ov = ET.fromstring(output_data['overlap']['second-post']['index.html']) self.assertEqual('html', ov.findall('.')[0].tag) self.assertEqual('Overlap Test', ov.findall('./head/title')[0].text.strip()) self.assertEqual('Second post', ov.findall('./body/h1')[0].text.strip()) ov = ET.fromstring( output_data['overlap']['andrew-second-post']['index.html']) self.assertEqual('html', ov.findall('.')[0].tag) self.assertEqual('Overlap Test', ov.findall('./head/title')[0].text.strip()) self.assertEqual("Andrew's Second Post", ov.findall('./body/h1')[0].text.strip()) # Test the Markdown table generation tables = ET.fromstring( output_data['2016']['06']['30']['tables-test']['index.html']) self.assertEqual('html', tables.findall('.')[0].tag) headings = tables.findall( "./body/div[@class='content']/table/thead/tr/th") self.assertEqual(3, len(headings)) self.assertEqual(['Heading 1', 'Heading 2', 'Heading 3'], [el.text.strip() for el in headings]) cells = tables.findall( "./body/div[@class='content']/table/tbody/tr/td") self.assertEqual(6, len(cells)) self.assertEqual(['One', 'Two', 'Three', 'Four', 'Five', 'Six'], [el.text.strip() for el in cells]) # Now test for the pagination pp = ET.fromstring(output_data['paged-posts']['1']['index.html']) self.assertEqual('html', pp.findall('.')[0].tag) self.assertEqual('Page 1 of 4', pp.findall('./head/title')[0].text.strip()) self.assertEqual('Page 1 of 4', pp.findall('./body/h1')[0].text.strip()) pp_els = pp.findall('./body/ul/li/a') pp_links = [el.attrib['href'] for el in pp_els] pp_link_titles = [el.text.strip() for el in pp_els] self.assertEqual( [ '/2018/02/12/unicode-in-markdown-test/', '/2018/01/20/test-datetime-format/' ], pp_links, ) self.assertEqual( [ 'Testing Unicode θ in Markdown content (issues #50 and #63)', 'Testing DateTime format (as per issue #59)' ], pp_link_titles, ) pp = ET.fromstring(output_data['paged-posts']['2']['index.html']) self.assertEqual('html', pp.findall('.')[0].tag) self.assertEqual('Page 2 of 4', pp.findall('./head/title')[0].text.strip()) self.assertEqual('Page 2 of 4', pp.findall('./body/h1')[0].text.strip()) pp_els = pp.findall('./body/ul/li/a') pp_links = [el.attrib['href'] for el in pp_els] pp_link_titles = [el.text.strip() for el in pp_els] self.assertEqual( ['/2016/06/30/tables-test/', '/2016/06/25/andrew-second-post/'], pp_links, ) self.assertEqual( ['Testing Markdown tables', 'Andrew\'s Second Post'], pp_link_titles, ) pp = ET.fromstring(output_data['paged-posts']['3']['index.html']) self.assertEqual('html', pp.findall('.')[0].tag) self.assertEqual('Page 3 of 4', pp.findall('./head/title')[0].text.strip()) self.assertEqual('Page 3 of 4', pp.findall('./body/h1')[0].text.strip()) pp_els = pp.findall('./body/ul/li/a') pp_links = [el.attrib['href'] for el in pp_els] pp_link_titles = [el.text.strip() for el in pp_els] self.assertEqual( ['/2016/06/18/second-post/', '/2016/06/15/my-first-post/'], pp_links, ) self.assertEqual( ['Second post', 'My first post'], pp_link_titles, ) pp = ET.fromstring(output_data['paged-posts']['4']['index.html']) self.assertEqual('html', pp.findall('.')[0].tag) self.assertEqual('Page 4 of 4', pp.findall('./head/title')[0].text.strip()) self.assertEqual('Page 4 of 4', pp.findall('./body/h1')[0].text.strip()) pp_els = pp.findall('./body/ul/li/a') pp_links = [el.attrib['href'] for el in pp_els] pp_link_titles = [el.text.strip() for el in pp_els] self.assertEqual( ['/2016/06/12/andrew-hello-world/'], pp_links, ) self.assertEqual( ['Andrew says Hello World'], pp_link_titles, ) self.assert_mlalchemy_complex_path_view_compiles(output_data) self.assert_homepage_compiles( self.assert_path_exists("mlalchemy/posts/index.html", output_data)) self.assert_json_data_compiles(output_data) self.assert_generated_js_compiles(output_data)
def main(): parser = argparse.ArgumentParser( description="Statik: a static web site generator for developers.") parser.add_argument( '--version', help='Display version info for Statik', action='store_true', ) parser.add_argument( '--quickstart', help= "Statik will generate a basic directory structure for you in the project directory and exit.", action='store_true', ) parser.add_argument( '-p', '--project', help= "The path to your Statik project or project YAML configuration file (default: current directory).", ) parser.add_argument( '-o', '--output', help= "The output path into which to place the built project (default: \"public\" directory in input " + "directory).", ) parser.add_argument( '-w', '--watch', help= "Statik will watch the project path for changes and automatically regenerate the project. " + "This also runs a small HTTP server to serve your output files.", action='store_true', ) parser.add_argument( '--host', help= "When watching a folder for changes (--watch), this specifies the host IP address or hostname to which " + "to bind (default: localhost).", default='localhost', ) parser.add_argument( '--port', help= "When watching a folder for changes (--watch), this specifies the port to which to bind (default: 8000).", type=int, default=8000, ) parser.add_argument( '-n', '--no-browser', action='store_true', default=False, help= "Do not attempt to automatically open a web browser at the served URL when watching for changes" ) parser.add_argument( '-s', '--safe-mode', action='store_true', default=False, help="Run Statik in safe mode (which disallows unsafe query execution)" ) parser.add_argument( '-v', '--verbose', help= "Whether or not to output verbose logging information (default: false).", action='store_true', ) parser.add_argument( '--quiet', default=False, help= "Run Statik in quiet mode, where there will be no output except upon error.", action='store_true') parser.add_argument( '--fail-silently', default=False, help= "Only relevant if running Statik in quiet mode - if Statik encounters an error, the only indication " "of this will be in the resulting error code returned by the process. No other output will be given " "on the terminal.", action='store_true') parser.add_argument( '--no-colorlog', action='store_true', help= "By default, Statik outputs logging data in colour. By specifying this switch, " + "coloured logging will be turned off.") parser.add_argument( '--clear-output', action='store_true', help= "Clears the output folder first prior to building the project. If watching " + "is initiated, this will only clear the output folder once-off.") args = parser.parse_args() error_context = StatikErrorContext() try: _project_path = args.project if args.project is not None else os.getcwd( ) project_path, config_file_path = get_project_config_file( _project_path, StatikProject.CONFIG_FILE) output_path = args.output if args.output is not None else \ os.path.join(project_path, 'public') configure_logging(verbose=args.verbose, quiet=args.quiet, fail_silently=args.fail_silently, colourise=not args.no_colorlog) if args.fail_silently and not args.quiet: logger.warning( "Ignoring --fail-silently switch because --quiet is not specified" ) if args.version: show_version() sys.exit(0) if args.clear_output: shutil.rmtree(output_path, ignore_errors=True) logger.info("Cleared output path: %s", output_path) if args.watch: watch(config_file_path, output_path, host=args.host, port=args.port, open_browser=(not args.no_browser), safe_mode=args.safe_mode, error_context=error_context) elif args.quickstart: generate_quickstart(project_path) else: generate(config_file_path, output_path=output_path, in_memory=False, safe_mode=args.safe_mode, error_context=error_context) except StatikError as e: sys.exit(e.exit_code) except Exception as e: logger.exception( "Exception caught while attempting to generate project") sys.exit(1) # success sys.exit(0)